@@ -440,11 +440,43 @@ func serveProd() error {
return <-errc
}
+const idleTimeout = 5 * time.Minute
+const activeTimeout = 10 * time.Minute
+
+// TODO: put this into the standard library and actually send
+// PING frames and GOAWAY, etc: golang.org/issue/14204
+func idleTimeoutHook() func(net.Conn, http.ConnState) {
+ var mu sync.Mutex
+ m := map[net.Conn]*time.Timer{}
+ return func(c net.Conn, cs http.ConnState) {
+ mu.Lock()
+ defer mu.Unlock()
+ if t, ok := m[c]; ok {
+ delete(m, c)
+ t.Stop()
+ }
+ var d time.Duration
+ switch cs {
+ case http.StateNew, http.StateIdle:
+ d = idleTimeout
+ case http.StateActive:
+ d = activeTimeout
+ default:
+ return
+ }
+ m[c] = time.AfterFunc(d, func() {
+ log.Printf("closing idle conn %v after %v", c.RemoteAddr(), d)
+ go c.Close()
+ })
+ }
+}
+
func main() {
var srv http.Server
flag.BoolVar(&http2.VerboseLogs, "verbose", false, "Verbose HTTP/2 debugging.")
flag.Parse()
srv.Addr = *httpsAddr
+ srv.ConnState = idleTimeoutHook()
registerHandlers()
diff --git a/Godeps/_workspace/src/golang.org/x/net/http2/h2i/h2i.go b/Godeps/_workspace/src/golang.org/x/net/http2/h2i/h2i.go
index c11b5a1a766..eadd1b182cf 100644
--- a/Godeps/_workspace/src/golang.org/x/net/http2/h2i/h2i.go
+++ b/Godeps/_workspace/src/golang.org/x/net/http2/h2i/h2i.go
@@ -78,7 +78,6 @@ var commands = map[string]command{
func usage() {
fmt.Fprintf(os.Stderr, "Usage: h2i \n\n")
flag.PrintDefaults()
- os.Exit(1)
}
// withPort adds ":443" if another port isn't already present.
@@ -111,6 +110,7 @@ func main() {
flag.Parse()
if flag.NArg() != 1 {
usage()
+ os.Exit(2)
}
log.SetFlags(0)
diff --git a/Godeps/_workspace/src/golang.org/x/net/http2/hpack/encode.go b/Godeps/_workspace/src/golang.org/x/net/http2/hpack/encode.go
index 80d621cf355..f9bb0339848 100644
--- a/Godeps/_workspace/src/golang.org/x/net/http2/hpack/encode.go
+++ b/Godeps/_workspace/src/golang.org/x/net/http2/hpack/encode.go
@@ -144,7 +144,7 @@ func (e *Encoder) SetMaxDynamicTableSizeLimit(v uint32) {
// shouldIndex reports whether f should be indexed.
func (e *Encoder) shouldIndex(f HeaderField) bool {
- return !f.Sensitive && f.size() <= e.dynTab.maxSize
+ return !f.Sensitive && f.Size() <= e.dynTab.maxSize
}
// appendIndexed appends index i, as encoded in "Indexed Header Field"
diff --git a/Godeps/_workspace/src/golang.org/x/net/http2/hpack/encode_test.go b/Godeps/_workspace/src/golang.org/x/net/http2/hpack/encode_test.go
deleted file mode 100644
index 92286f3bad1..00000000000
--- a/Godeps/_workspace/src/golang.org/x/net/http2/hpack/encode_test.go
+++ /dev/null
@@ -1,330 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package hpack
-
-import (
- "bytes"
- "encoding/hex"
- "reflect"
- "strings"
- "testing"
-)
-
-func TestEncoderTableSizeUpdate(t *testing.T) {
- tests := []struct {
- size1, size2 uint32
- wantHex string
- }{
- // Should emit 2 table size updates (2048 and 4096)
- {2048, 4096, "3fe10f 3fe11f 82"},
-
- // Should emit 1 table size update (2048)
- {16384, 2048, "3fe10f 82"},
- }
- for _, tt := range tests {
- var buf bytes.Buffer
- e := NewEncoder(&buf)
- e.SetMaxDynamicTableSize(tt.size1)
- e.SetMaxDynamicTableSize(tt.size2)
- if err := e.WriteField(pair(":method", "GET")); err != nil {
- t.Fatal(err)
- }
- want := removeSpace(tt.wantHex)
- if got := hex.EncodeToString(buf.Bytes()); got != want {
- t.Errorf("e.SetDynamicTableSize %v, %v = %q; want %q", tt.size1, tt.size2, got, want)
- }
- }
-}
-
-func TestEncoderWriteField(t *testing.T) {
- var buf bytes.Buffer
- e := NewEncoder(&buf)
- var got []HeaderField
- d := NewDecoder(4<<10, func(f HeaderField) {
- got = append(got, f)
- })
-
- tests := []struct {
- hdrs []HeaderField
- }{
- {[]HeaderField{
- pair(":method", "GET"),
- pair(":scheme", "http"),
- pair(":path", "/"),
- pair(":authority", "www.example.com"),
- }},
- {[]HeaderField{
- pair(":method", "GET"),
- pair(":scheme", "http"),
- pair(":path", "/"),
- pair(":authority", "www.example.com"),
- pair("cache-control", "no-cache"),
- }},
- {[]HeaderField{
- pair(":method", "GET"),
- pair(":scheme", "https"),
- pair(":path", "/index.html"),
- pair(":authority", "www.example.com"),
- pair("custom-key", "custom-value"),
- }},
- }
- for i, tt := range tests {
- buf.Reset()
- got = got[:0]
- for _, hf := range tt.hdrs {
- if err := e.WriteField(hf); err != nil {
- t.Fatal(err)
- }
- }
- _, err := d.Write(buf.Bytes())
- if err != nil {
- t.Errorf("%d. Decoder Write = %v", i, err)
- }
- if !reflect.DeepEqual(got, tt.hdrs) {
- t.Errorf("%d. Decoded %+v; want %+v", i, got, tt.hdrs)
- }
- }
-}
-
-func TestEncoderSearchTable(t *testing.T) {
- e := NewEncoder(nil)
-
- e.dynTab.add(pair("foo", "bar"))
- e.dynTab.add(pair("blake", "miz"))
- e.dynTab.add(pair(":method", "GET"))
-
- tests := []struct {
- hf HeaderField
- wantI uint64
- wantMatch bool
- }{
- // Name and Value match
- {pair("foo", "bar"), uint64(len(staticTable) + 3), true},
- {pair("blake", "miz"), uint64(len(staticTable) + 2), true},
- {pair(":method", "GET"), 2, true},
-
- // Only name match because Sensitive == true
- {HeaderField{":method", "GET", true}, 2, false},
-
- // Only Name matches
- {pair("foo", "..."), uint64(len(staticTable) + 3), false},
- {pair("blake", "..."), uint64(len(staticTable) + 2), false},
- {pair(":method", "..."), 2, false},
-
- // None match
- {pair("foo-", "bar"), 0, false},
- }
- for _, tt := range tests {
- if gotI, gotMatch := e.searchTable(tt.hf); gotI != tt.wantI || gotMatch != tt.wantMatch {
- t.Errorf("d.search(%+v) = %v, %v; want %v, %v", tt.hf, gotI, gotMatch, tt.wantI, tt.wantMatch)
- }
- }
-}
-
-func TestAppendVarInt(t *testing.T) {
- tests := []struct {
- n byte
- i uint64
- want []byte
- }{
- // Fits in a byte:
- {1, 0, []byte{0}},
- {2, 2, []byte{2}},
- {3, 6, []byte{6}},
- {4, 14, []byte{14}},
- {5, 30, []byte{30}},
- {6, 62, []byte{62}},
- {7, 126, []byte{126}},
- {8, 254, []byte{254}},
-
- // Multiple bytes:
- {5, 1337, []byte{31, 154, 10}},
- }
- for _, tt := range tests {
- got := appendVarInt(nil, tt.n, tt.i)
- if !bytes.Equal(got, tt.want) {
- t.Errorf("appendVarInt(nil, %v, %v) = %v; want %v", tt.n, tt.i, got, tt.want)
- }
- }
-}
-
-func TestAppendHpackString(t *testing.T) {
- tests := []struct {
- s, wantHex string
- }{
- // Huffman encoded
- {"www.example.com", "8c f1e3 c2e5 f23a 6ba0 ab90 f4ff"},
-
- // Not Huffman encoded
- {"a", "01 61"},
-
- // zero length
- {"", "00"},
- }
- for _, tt := range tests {
- want := removeSpace(tt.wantHex)
- buf := appendHpackString(nil, tt.s)
- if got := hex.EncodeToString(buf); want != got {
- t.Errorf("appendHpackString(nil, %q) = %q; want %q", tt.s, got, want)
- }
- }
-}
-
-func TestAppendIndexed(t *testing.T) {
- tests := []struct {
- i uint64
- wantHex string
- }{
- // 1 byte
- {1, "81"},
- {126, "fe"},
-
- // 2 bytes
- {127, "ff00"},
- {128, "ff01"},
- }
- for _, tt := range tests {
- want := removeSpace(tt.wantHex)
- buf := appendIndexed(nil, tt.i)
- if got := hex.EncodeToString(buf); want != got {
- t.Errorf("appendIndex(nil, %v) = %q; want %q", tt.i, got, want)
- }
- }
-}
-
-func TestAppendNewName(t *testing.T) {
- tests := []struct {
- f HeaderField
- indexing bool
- wantHex string
- }{
- // Incremental indexing
- {HeaderField{"custom-key", "custom-value", false}, true, "40 88 25a8 49e9 5ba9 7d7f 89 25a8 49e9 5bb8 e8b4 bf"},
-
- // Without indexing
- {HeaderField{"custom-key", "custom-value", false}, false, "00 88 25a8 49e9 5ba9 7d7f 89 25a8 49e9 5bb8 e8b4 bf"},
-
- // Never indexed
- {HeaderField{"custom-key", "custom-value", true}, true, "10 88 25a8 49e9 5ba9 7d7f 89 25a8 49e9 5bb8 e8b4 bf"},
- {HeaderField{"custom-key", "custom-value", true}, false, "10 88 25a8 49e9 5ba9 7d7f 89 25a8 49e9 5bb8 e8b4 bf"},
- }
- for _, tt := range tests {
- want := removeSpace(tt.wantHex)
- buf := appendNewName(nil, tt.f, tt.indexing)
- if got := hex.EncodeToString(buf); want != got {
- t.Errorf("appendNewName(nil, %+v, %v) = %q; want %q", tt.f, tt.indexing, got, want)
- }
- }
-}
-
-func TestAppendIndexedName(t *testing.T) {
- tests := []struct {
- f HeaderField
- i uint64
- indexing bool
- wantHex string
- }{
- // Incremental indexing
- {HeaderField{":status", "302", false}, 8, true, "48 82 6402"},
-
- // Without indexing
- {HeaderField{":status", "302", false}, 8, false, "08 82 6402"},
-
- // Never indexed
- {HeaderField{":status", "302", true}, 8, true, "18 82 6402"},
- {HeaderField{":status", "302", true}, 8, false, "18 82 6402"},
- }
- for _, tt := range tests {
- want := removeSpace(tt.wantHex)
- buf := appendIndexedName(nil, tt.f, tt.i, tt.indexing)
- if got := hex.EncodeToString(buf); want != got {
- t.Errorf("appendIndexedName(nil, %+v, %v) = %q; want %q", tt.f, tt.indexing, got, want)
- }
- }
-}
-
-func TestAppendTableSize(t *testing.T) {
- tests := []struct {
- i uint32
- wantHex string
- }{
- // Fits into 1 byte
- {30, "3e"},
-
- // Extra byte
- {31, "3f00"},
- {32, "3f01"},
- }
- for _, tt := range tests {
- want := removeSpace(tt.wantHex)
- buf := appendTableSize(nil, tt.i)
- if got := hex.EncodeToString(buf); want != got {
- t.Errorf("appendTableSize(nil, %v) = %q; want %q", tt.i, got, want)
- }
- }
-}
-
-func TestEncoderSetMaxDynamicTableSize(t *testing.T) {
- var buf bytes.Buffer
- e := NewEncoder(&buf)
- tests := []struct {
- v uint32
- wantUpdate bool
- wantMinSize uint32
- wantMaxSize uint32
- }{
- // Set new table size to 2048
- {2048, true, 2048, 2048},
-
- // Set new table size to 16384, but still limited to
- // 4096
- {16384, true, 2048, 4096},
- }
- for _, tt := range tests {
- e.SetMaxDynamicTableSize(tt.v)
- if got := e.tableSizeUpdate; tt.wantUpdate != got {
- t.Errorf("e.tableSizeUpdate = %v; want %v", got, tt.wantUpdate)
- }
- if got := e.minSize; tt.wantMinSize != got {
- t.Errorf("e.minSize = %v; want %v", got, tt.wantMinSize)
- }
- if got := e.dynTab.maxSize; tt.wantMaxSize != got {
- t.Errorf("e.maxSize = %v; want %v", got, tt.wantMaxSize)
- }
- }
-}
-
-func TestEncoderSetMaxDynamicTableSizeLimit(t *testing.T) {
- e := NewEncoder(nil)
- // 4095 < initialHeaderTableSize means maxSize is truncated to
- // 4095.
- e.SetMaxDynamicTableSizeLimit(4095)
- if got, want := e.dynTab.maxSize, uint32(4095); got != want {
- t.Errorf("e.dynTab.maxSize = %v; want %v", got, want)
- }
- if got, want := e.maxSizeLimit, uint32(4095); got != want {
- t.Errorf("e.maxSizeLimit = %v; want %v", got, want)
- }
- if got, want := e.tableSizeUpdate, true; got != want {
- t.Errorf("e.tableSizeUpdate = %v; want %v", got, want)
- }
- // maxSize will be truncated to maxSizeLimit
- e.SetMaxDynamicTableSize(16384)
- if got, want := e.dynTab.maxSize, uint32(4095); got != want {
- t.Errorf("e.dynTab.maxSize = %v; want %v", got, want)
- }
- // 8192 > current maxSizeLimit, so maxSize does not change.
- e.SetMaxDynamicTableSizeLimit(8192)
- if got, want := e.dynTab.maxSize, uint32(4095); got != want {
- t.Errorf("e.dynTab.maxSize = %v; want %v", got, want)
- }
- if got, want := e.maxSizeLimit, uint32(8192); got != want {
- t.Errorf("e.maxSizeLimit = %v; want %v", got, want)
- }
-}
-
-func removeSpace(s string) string {
- return strings.Replace(s, " ", "", -1)
-}
diff --git a/Godeps/_workspace/src/golang.org/x/net/http2/hpack/hpack.go b/Godeps/_workspace/src/golang.org/x/net/http2/hpack/hpack.go
index 2ea4949ab08..dcf257afa41 100644
--- a/Godeps/_workspace/src/golang.org/x/net/http2/hpack/hpack.go
+++ b/Godeps/_workspace/src/golang.org/x/net/http2/hpack/hpack.go
@@ -41,6 +41,14 @@ type HeaderField struct {
Sensitive bool
}
+// IsPseudo reports whether the header field is an http2 pseudo header.
+// That is, it reports whether it starts with a colon.
+// It is not otherwise guaranteed to be a valid psuedo header field,
+// though.
+func (hf HeaderField) IsPseudo() bool {
+ return len(hf.Name) != 0 && hf.Name[0] == ':'
+}
+
func (hf HeaderField) String() string {
var suffix string
if hf.Sensitive {
@@ -49,7 +57,8 @@ func (hf HeaderField) String() string {
return fmt.Sprintf("header field %q = %q%s", hf.Name, hf.Value, suffix)
}
-func (hf *HeaderField) size() uint32 {
+// Size returns the size of an entry per RFC 7540 section 5.2.
+func (hf HeaderField) Size() uint32 {
// http://http2.github.io/http2-spec/compression.html#rfc.section.4.1
// "The size of the dynamic table is the sum of the size of
// its entries. The size of an entry is the sum of its name's
@@ -171,7 +180,7 @@ func (dt *dynamicTable) setMaxSize(v uint32) {
func (dt *dynamicTable) add(f HeaderField) {
dt.ents = append(dt.ents, f)
- dt.size += f.size()
+ dt.size += f.Size()
dt.evict()
}
@@ -179,7 +188,7 @@ func (dt *dynamicTable) add(f HeaderField) {
func (dt *dynamicTable) evict() {
base := dt.ents // keep base pointer of slice
for dt.size > dt.maxSize {
- dt.size -= dt.ents[0].size()
+ dt.size -= dt.ents[0].Size()
dt.ents = dt.ents[1:]
}
diff --git a/Godeps/_workspace/src/golang.org/x/net/http2/hpack/hpack_test.go b/Godeps/_workspace/src/golang.org/x/net/http2/hpack/hpack_test.go
deleted file mode 100644
index 6dc69f95799..00000000000
--- a/Godeps/_workspace/src/golang.org/x/net/http2/hpack/hpack_test.go
+++ /dev/null
@@ -1,813 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package hpack
-
-import (
- "bufio"
- "bytes"
- "encoding/hex"
- "fmt"
- "math/rand"
- "reflect"
- "regexp"
- "strconv"
- "strings"
- "testing"
- "time"
-)
-
-func TestStaticTable(t *testing.T) {
- fromSpec := `
- +-------+-----------------------------+---------------+
- | 1 | :authority | |
- | 2 | :method | GET |
- | 3 | :method | POST |
- | 4 | :path | / |
- | 5 | :path | /index.html |
- | 6 | :scheme | http |
- | 7 | :scheme | https |
- | 8 | :status | 200 |
- | 9 | :status | 204 |
- | 10 | :status | 206 |
- | 11 | :status | 304 |
- | 12 | :status | 400 |
- | 13 | :status | 404 |
- | 14 | :status | 500 |
- | 15 | accept-charset | |
- | 16 | accept-encoding | gzip, deflate |
- | 17 | accept-language | |
- | 18 | accept-ranges | |
- | 19 | accept | |
- | 20 | access-control-allow-origin | |
- | 21 | age | |
- | 22 | allow | |
- | 23 | authorization | |
- | 24 | cache-control | |
- | 25 | content-disposition | |
- | 26 | content-encoding | |
- | 27 | content-language | |
- | 28 | content-length | |
- | 29 | content-location | |
- | 30 | content-range | |
- | 31 | content-type | |
- | 32 | cookie | |
- | 33 | date | |
- | 34 | etag | |
- | 35 | expect | |
- | 36 | expires | |
- | 37 | from | |
- | 38 | host | |
- | 39 | if-match | |
- | 40 | if-modified-since | |
- | 41 | if-none-match | |
- | 42 | if-range | |
- | 43 | if-unmodified-since | |
- | 44 | last-modified | |
- | 45 | link | |
- | 46 | location | |
- | 47 | max-forwards | |
- | 48 | proxy-authenticate | |
- | 49 | proxy-authorization | |
- | 50 | range | |
- | 51 | referer | |
- | 52 | refresh | |
- | 53 | retry-after | |
- | 54 | server | |
- | 55 | set-cookie | |
- | 56 | strict-transport-security | |
- | 57 | transfer-encoding | |
- | 58 | user-agent | |
- | 59 | vary | |
- | 60 | via | |
- | 61 | www-authenticate | |
- +-------+-----------------------------+---------------+
-`
- bs := bufio.NewScanner(strings.NewReader(fromSpec))
- re := regexp.MustCompile(`\| (\d+)\s+\| (\S+)\s*\| (\S(.*\S)?)?\s+\|`)
- for bs.Scan() {
- l := bs.Text()
- if !strings.Contains(l, "|") {
- continue
- }
- m := re.FindStringSubmatch(l)
- if m == nil {
- continue
- }
- i, err := strconv.Atoi(m[1])
- if err != nil {
- t.Errorf("Bogus integer on line %q", l)
- continue
- }
- if i < 1 || i > len(staticTable) {
- t.Errorf("Bogus index %d on line %q", i, l)
- continue
- }
- if got, want := staticTable[i-1].Name, m[2]; got != want {
- t.Errorf("header index %d name = %q; want %q", i, got, want)
- }
- if got, want := staticTable[i-1].Value, m[3]; got != want {
- t.Errorf("header index %d value = %q; want %q", i, got, want)
- }
- }
- if err := bs.Err(); err != nil {
- t.Error(err)
- }
-}
-
-func (d *Decoder) mustAt(idx int) HeaderField {
- if hf, ok := d.at(uint64(idx)); !ok {
- panic(fmt.Sprintf("bogus index %d", idx))
- } else {
- return hf
- }
-}
-
-func TestDynamicTableAt(t *testing.T) {
- d := NewDecoder(4096, nil)
- at := d.mustAt
- if got, want := at(2), (pair(":method", "GET")); got != want {
- t.Errorf("at(2) = %v; want %v", got, want)
- }
- d.dynTab.add(pair("foo", "bar"))
- d.dynTab.add(pair("blake", "miz"))
- if got, want := at(len(staticTable)+1), (pair("blake", "miz")); got != want {
- t.Errorf("at(dyn 1) = %v; want %v", got, want)
- }
- if got, want := at(len(staticTable)+2), (pair("foo", "bar")); got != want {
- t.Errorf("at(dyn 2) = %v; want %v", got, want)
- }
- if got, want := at(3), (pair(":method", "POST")); got != want {
- t.Errorf("at(3) = %v; want %v", got, want)
- }
-}
-
-func TestDynamicTableSearch(t *testing.T) {
- dt := dynamicTable{}
- dt.setMaxSize(4096)
-
- dt.add(pair("foo", "bar"))
- dt.add(pair("blake", "miz"))
- dt.add(pair(":method", "GET"))
-
- tests := []struct {
- hf HeaderField
- wantI uint64
- wantMatch bool
- }{
- // Name and Value match
- {pair("foo", "bar"), 3, true},
- {pair(":method", "GET"), 1, true},
-
- // Only name match because of Sensitive == true
- {HeaderField{"blake", "miz", true}, 2, false},
-
- // Only Name matches
- {pair("foo", "..."), 3, false},
- {pair("blake", "..."), 2, false},
- {pair(":method", "..."), 1, false},
-
- // None match
- {pair("foo-", "bar"), 0, false},
- }
- for _, tt := range tests {
- if gotI, gotMatch := dt.search(tt.hf); gotI != tt.wantI || gotMatch != tt.wantMatch {
- t.Errorf("d.search(%+v) = %v, %v; want %v, %v", tt.hf, gotI, gotMatch, tt.wantI, tt.wantMatch)
- }
- }
-}
-
-func TestDynamicTableSizeEvict(t *testing.T) {
- d := NewDecoder(4096, nil)
- if want := uint32(0); d.dynTab.size != want {
- t.Fatalf("size = %d; want %d", d.dynTab.size, want)
- }
- add := d.dynTab.add
- add(pair("blake", "eats pizza"))
- if want := uint32(15 + 32); d.dynTab.size != want {
- t.Fatalf("after pizza, size = %d; want %d", d.dynTab.size, want)
- }
- add(pair("foo", "bar"))
- if want := uint32(15 + 32 + 6 + 32); d.dynTab.size != want {
- t.Fatalf("after foo bar, size = %d; want %d", d.dynTab.size, want)
- }
- d.dynTab.setMaxSize(15 + 32 + 1 /* slop */)
- if want := uint32(6 + 32); d.dynTab.size != want {
- t.Fatalf("after setMaxSize, size = %d; want %d", d.dynTab.size, want)
- }
- if got, want := d.mustAt(len(staticTable)+1), (pair("foo", "bar")); got != want {
- t.Errorf("at(dyn 1) = %v; want %v", got, want)
- }
- add(pair("long", strings.Repeat("x", 500)))
- if want := uint32(0); d.dynTab.size != want {
- t.Fatalf("after big one, size = %d; want %d", d.dynTab.size, want)
- }
-}
-
-func TestDecoderDecode(t *testing.T) {
- tests := []struct {
- name string
- in []byte
- want []HeaderField
- wantDynTab []HeaderField // newest entry first
- }{
- // C.2.1 Literal Header Field with Indexing
- // http://http2.github.io/http2-spec/compression.html#rfc.section.C.2.1
- {"C.2.1", dehex("400a 6375 7374 6f6d 2d6b 6579 0d63 7573 746f 6d2d 6865 6164 6572"),
- []HeaderField{pair("custom-key", "custom-header")},
- []HeaderField{pair("custom-key", "custom-header")},
- },
-
- // C.2.2 Literal Header Field without Indexing
- // http://http2.github.io/http2-spec/compression.html#rfc.section.C.2.2
- {"C.2.2", dehex("040c 2f73 616d 706c 652f 7061 7468"),
- []HeaderField{pair(":path", "/sample/path")},
- []HeaderField{}},
-
- // C.2.3 Literal Header Field never Indexed
- // http://http2.github.io/http2-spec/compression.html#rfc.section.C.2.3
- {"C.2.3", dehex("1008 7061 7373 776f 7264 0673 6563 7265 74"),
- []HeaderField{{"password", "secret", true}},
- []HeaderField{}},
-
- // C.2.4 Indexed Header Field
- // http://http2.github.io/http2-spec/compression.html#rfc.section.C.2.4
- {"C.2.4", []byte("\x82"),
- []HeaderField{pair(":method", "GET")},
- []HeaderField{}},
- }
- for _, tt := range tests {
- d := NewDecoder(4096, nil)
- hf, err := d.DecodeFull(tt.in)
- if err != nil {
- t.Errorf("%s: %v", tt.name, err)
- continue
- }
- if !reflect.DeepEqual(hf, tt.want) {
- t.Errorf("%s: Got %v; want %v", tt.name, hf, tt.want)
- }
- gotDynTab := d.dynTab.reverseCopy()
- if !reflect.DeepEqual(gotDynTab, tt.wantDynTab) {
- t.Errorf("%s: dynamic table after = %v; want %v", tt.name, gotDynTab, tt.wantDynTab)
- }
- }
-}
-
-func (dt *dynamicTable) reverseCopy() (hf []HeaderField) {
- hf = make([]HeaderField, len(dt.ents))
- for i := range hf {
- hf[i] = dt.ents[len(dt.ents)-1-i]
- }
- return
-}
-
-type encAndWant struct {
- enc []byte
- want []HeaderField
- wantDynTab []HeaderField
- wantDynSize uint32
-}
-
-// C.3 Request Examples without Huffman Coding
-// http://http2.github.io/http2-spec/compression.html#rfc.section.C.3
-func TestDecodeC3_NoHuffman(t *testing.T) {
- testDecodeSeries(t, 4096, []encAndWant{
- {dehex("8286 8441 0f77 7777 2e65 7861 6d70 6c65 2e63 6f6d"),
- []HeaderField{
- pair(":method", "GET"),
- pair(":scheme", "http"),
- pair(":path", "/"),
- pair(":authority", "www.example.com"),
- },
- []HeaderField{
- pair(":authority", "www.example.com"),
- },
- 57,
- },
- {dehex("8286 84be 5808 6e6f 2d63 6163 6865"),
- []HeaderField{
- pair(":method", "GET"),
- pair(":scheme", "http"),
- pair(":path", "/"),
- pair(":authority", "www.example.com"),
- pair("cache-control", "no-cache"),
- },
- []HeaderField{
- pair("cache-control", "no-cache"),
- pair(":authority", "www.example.com"),
- },
- 110,
- },
- {dehex("8287 85bf 400a 6375 7374 6f6d 2d6b 6579 0c63 7573 746f 6d2d 7661 6c75 65"),
- []HeaderField{
- pair(":method", "GET"),
- pair(":scheme", "https"),
- pair(":path", "/index.html"),
- pair(":authority", "www.example.com"),
- pair("custom-key", "custom-value"),
- },
- []HeaderField{
- pair("custom-key", "custom-value"),
- pair("cache-control", "no-cache"),
- pair(":authority", "www.example.com"),
- },
- 164,
- },
- })
-}
-
-// C.4 Request Examples with Huffman Coding
-// http://http2.github.io/http2-spec/compression.html#rfc.section.C.4
-func TestDecodeC4_Huffman(t *testing.T) {
- testDecodeSeries(t, 4096, []encAndWant{
- {dehex("8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4 ff"),
- []HeaderField{
- pair(":method", "GET"),
- pair(":scheme", "http"),
- pair(":path", "/"),
- pair(":authority", "www.example.com"),
- },
- []HeaderField{
- pair(":authority", "www.example.com"),
- },
- 57,
- },
- {dehex("8286 84be 5886 a8eb 1064 9cbf"),
- []HeaderField{
- pair(":method", "GET"),
- pair(":scheme", "http"),
- pair(":path", "/"),
- pair(":authority", "www.example.com"),
- pair("cache-control", "no-cache"),
- },
- []HeaderField{
- pair("cache-control", "no-cache"),
- pair(":authority", "www.example.com"),
- },
- 110,
- },
- {dehex("8287 85bf 4088 25a8 49e9 5ba9 7d7f 8925 a849 e95b b8e8 b4bf"),
- []HeaderField{
- pair(":method", "GET"),
- pair(":scheme", "https"),
- pair(":path", "/index.html"),
- pair(":authority", "www.example.com"),
- pair("custom-key", "custom-value"),
- },
- []HeaderField{
- pair("custom-key", "custom-value"),
- pair("cache-control", "no-cache"),
- pair(":authority", "www.example.com"),
- },
- 164,
- },
- })
-}
-
-// http://http2.github.io/http2-spec/compression.html#rfc.section.C.5
-// "This section shows several consecutive header lists, corresponding
-// to HTTP responses, on the same connection. The HTTP/2 setting
-// parameter SETTINGS_HEADER_TABLE_SIZE is set to the value of 256
-// octets, causing some evictions to occur."
-func TestDecodeC5_ResponsesNoHuff(t *testing.T) {
- testDecodeSeries(t, 256, []encAndWant{
- {dehex(`
-4803 3330 3258 0770 7269 7661 7465 611d
-4d6f 6e2c 2032 3120 4f63 7420 3230 3133
-2032 303a 3133 3a32 3120 474d 546e 1768
-7474 7073 3a2f 2f77 7777 2e65 7861 6d70
-6c65 2e63 6f6d
-`),
- []HeaderField{
- pair(":status", "302"),
- pair("cache-control", "private"),
- pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
- pair("location", "https://www.example.com"),
- },
- []HeaderField{
- pair("location", "https://www.example.com"),
- pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
- pair("cache-control", "private"),
- pair(":status", "302"),
- },
- 222,
- },
- {dehex("4803 3330 37c1 c0bf"),
- []HeaderField{
- pair(":status", "307"),
- pair("cache-control", "private"),
- pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
- pair("location", "https://www.example.com"),
- },
- []HeaderField{
- pair(":status", "307"),
- pair("location", "https://www.example.com"),
- pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
- pair("cache-control", "private"),
- },
- 222,
- },
- {dehex(`
-88c1 611d 4d6f 6e2c 2032 3120 4f63 7420
-3230 3133 2032 303a 3133 3a32 3220 474d
-54c0 5a04 677a 6970 7738 666f 6f3d 4153
-444a 4b48 514b 425a 584f 5157 454f 5049
-5541 5851 5745 4f49 553b 206d 6178 2d61
-6765 3d33 3630 303b 2076 6572 7369 6f6e
-3d31
-`),
- []HeaderField{
- pair(":status", "200"),
- pair("cache-control", "private"),
- pair("date", "Mon, 21 Oct 2013 20:13:22 GMT"),
- pair("location", "https://www.example.com"),
- pair("content-encoding", "gzip"),
- pair("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"),
- },
- []HeaderField{
- pair("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"),
- pair("content-encoding", "gzip"),
- pair("date", "Mon, 21 Oct 2013 20:13:22 GMT"),
- },
- 215,
- },
- })
-}
-
-// http://http2.github.io/http2-spec/compression.html#rfc.section.C.6
-// "This section shows the same examples as the previous section, but
-// using Huffman encoding for the literal values. The HTTP/2 setting
-// parameter SETTINGS_HEADER_TABLE_SIZE is set to the value of 256
-// octets, causing some evictions to occur. The eviction mechanism
-// uses the length of the decoded literal values, so the same
-// evictions occurs as in the previous section."
-func TestDecodeC6_ResponsesHuffman(t *testing.T) {
- testDecodeSeries(t, 256, []encAndWant{
- {dehex(`
-4882 6402 5885 aec3 771a 4b61 96d0 7abe
-9410 54d4 44a8 2005 9504 0b81 66e0 82a6
-2d1b ff6e 919d 29ad 1718 63c7 8f0b 97c8
-e9ae 82ae 43d3
-`),
- []HeaderField{
- pair(":status", "302"),
- pair("cache-control", "private"),
- pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
- pair("location", "https://www.example.com"),
- },
- []HeaderField{
- pair("location", "https://www.example.com"),
- pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
- pair("cache-control", "private"),
- pair(":status", "302"),
- },
- 222,
- },
- {dehex("4883 640e ffc1 c0bf"),
- []HeaderField{
- pair(":status", "307"),
- pair("cache-control", "private"),
- pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
- pair("location", "https://www.example.com"),
- },
- []HeaderField{
- pair(":status", "307"),
- pair("location", "https://www.example.com"),
- pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
- pair("cache-control", "private"),
- },
- 222,
- },
- {dehex(`
-88c1 6196 d07a be94 1054 d444 a820 0595
-040b 8166 e084 a62d 1bff c05a 839b d9ab
-77ad 94e7 821d d7f2 e6c7 b335 dfdf cd5b
-3960 d5af 2708 7f36 72c1 ab27 0fb5 291f
-9587 3160 65c0 03ed 4ee5 b106 3d50 07
-`),
- []HeaderField{
- pair(":status", "200"),
- pair("cache-control", "private"),
- pair("date", "Mon, 21 Oct 2013 20:13:22 GMT"),
- pair("location", "https://www.example.com"),
- pair("content-encoding", "gzip"),
- pair("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"),
- },
- []HeaderField{
- pair("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"),
- pair("content-encoding", "gzip"),
- pair("date", "Mon, 21 Oct 2013 20:13:22 GMT"),
- },
- 215,
- },
- })
-}
-
-func testDecodeSeries(t *testing.T, size uint32, steps []encAndWant) {
- d := NewDecoder(size, nil)
- for i, step := range steps {
- hf, err := d.DecodeFull(step.enc)
- if err != nil {
- t.Fatalf("Error at step index %d: %v", i, err)
- }
- if !reflect.DeepEqual(hf, step.want) {
- t.Fatalf("At step index %d: Got headers %v; want %v", i, hf, step.want)
- }
- gotDynTab := d.dynTab.reverseCopy()
- if !reflect.DeepEqual(gotDynTab, step.wantDynTab) {
- t.Errorf("After step index %d, dynamic table = %v; want %v", i, gotDynTab, step.wantDynTab)
- }
- if d.dynTab.size != step.wantDynSize {
- t.Errorf("After step index %d, dynamic table size = %v; want %v", i, d.dynTab.size, step.wantDynSize)
- }
- }
-}
-
-func TestHuffmanDecode(t *testing.T) {
- tests := []struct {
- inHex, want string
- }{
- {"f1e3 c2e5 f23a 6ba0 ab90 f4ff", "www.example.com"},
- {"a8eb 1064 9cbf", "no-cache"},
- {"25a8 49e9 5ba9 7d7f", "custom-key"},
- {"25a8 49e9 5bb8 e8b4 bf", "custom-value"},
- {"6402", "302"},
- {"aec3 771a 4b", "private"},
- {"d07a be94 1054 d444 a820 0595 040b 8166 e082 a62d 1bff", "Mon, 21 Oct 2013 20:13:21 GMT"},
- {"9d29 ad17 1863 c78f 0b97 c8e9 ae82 ae43 d3", "https://www.example.com"},
- {"9bd9 ab", "gzip"},
- {"94e7 821d d7f2 e6c7 b335 dfdf cd5b 3960 d5af 2708 7f36 72c1 ab27 0fb5 291f 9587 3160 65c0 03ed 4ee5 b106 3d50 07",
- "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"},
- }
- for i, tt := range tests {
- var buf bytes.Buffer
- in, err := hex.DecodeString(strings.Replace(tt.inHex, " ", "", -1))
- if err != nil {
- t.Errorf("%d. hex input error: %v", i, err)
- continue
- }
- if _, err := HuffmanDecode(&buf, in); err != nil {
- t.Errorf("%d. decode error: %v", i, err)
- continue
- }
- if got := buf.String(); tt.want != got {
- t.Errorf("%d. decode = %q; want %q", i, got, tt.want)
- }
- }
-}
-
-func TestAppendHuffmanString(t *testing.T) {
- tests := []struct {
- in, want string
- }{
- {"www.example.com", "f1e3 c2e5 f23a 6ba0 ab90 f4ff"},
- {"no-cache", "a8eb 1064 9cbf"},
- {"custom-key", "25a8 49e9 5ba9 7d7f"},
- {"custom-value", "25a8 49e9 5bb8 e8b4 bf"},
- {"302", "6402"},
- {"private", "aec3 771a 4b"},
- {"Mon, 21 Oct 2013 20:13:21 GMT", "d07a be94 1054 d444 a820 0595 040b 8166 e082 a62d 1bff"},
- {"https://www.example.com", "9d29 ad17 1863 c78f 0b97 c8e9 ae82 ae43 d3"},
- {"gzip", "9bd9 ab"},
- {"foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1",
- "94e7 821d d7f2 e6c7 b335 dfdf cd5b 3960 d5af 2708 7f36 72c1 ab27 0fb5 291f 9587 3160 65c0 03ed 4ee5 b106 3d50 07"},
- }
- for i, tt := range tests {
- buf := []byte{}
- want := strings.Replace(tt.want, " ", "", -1)
- buf = AppendHuffmanString(buf, tt.in)
- if got := hex.EncodeToString(buf); want != got {
- t.Errorf("%d. encode = %q; want %q", i, got, want)
- }
- }
-}
-
-func TestHuffmanMaxStrLen(t *testing.T) {
- const msg = "Some string"
- huff := AppendHuffmanString(nil, msg)
-
- testGood := func(max int) {
- var out bytes.Buffer
- if err := huffmanDecode(&out, max, huff); err != nil {
- t.Errorf("For maxLen=%d, unexpected error: %v", max, err)
- }
- if out.String() != msg {
- t.Errorf("For maxLen=%d, out = %q; want %q", max, out.String(), msg)
- }
- }
- testGood(0)
- testGood(len(msg))
- testGood(len(msg) + 1)
-
- var out bytes.Buffer
- if err := huffmanDecode(&out, len(msg)-1, huff); err != ErrStringLength {
- t.Errorf("err = %v; want ErrStringLength", err)
- }
-}
-
-func TestHuffmanRoundtripStress(t *testing.T) {
- const Len = 50 // of uncompressed string
- input := make([]byte, Len)
- var output bytes.Buffer
- var huff []byte
-
- n := 5000
- if testing.Short() {
- n = 100
- }
- seed := time.Now().UnixNano()
- t.Logf("Seed = %v", seed)
- src := rand.New(rand.NewSource(seed))
- var encSize int64
- for i := 0; i < n; i++ {
- for l := range input {
- input[l] = byte(src.Intn(256))
- }
- huff = AppendHuffmanString(huff[:0], string(input))
- encSize += int64(len(huff))
- output.Reset()
- if err := huffmanDecode(&output, 0, huff); err != nil {
- t.Errorf("Failed to decode %q -> %q -> error %v", input, huff, err)
- continue
- }
- if !bytes.Equal(output.Bytes(), input) {
- t.Errorf("Roundtrip failure on %q -> %q -> %q", input, huff, output.Bytes())
- }
- }
- t.Logf("Compressed size of original: %0.02f%% (%v -> %v)", 100*(float64(encSize)/(Len*float64(n))), Len*n, encSize)
-}
-
-func TestHuffmanDecodeFuzz(t *testing.T) {
- const Len = 50 // of compressed
- var buf, zbuf bytes.Buffer
-
- n := 5000
- if testing.Short() {
- n = 100
- }
- seed := time.Now().UnixNano()
- t.Logf("Seed = %v", seed)
- src := rand.New(rand.NewSource(seed))
- numFail := 0
- for i := 0; i < n; i++ {
- zbuf.Reset()
- if i == 0 {
- // Start with at least one invalid one.
- zbuf.WriteString("00\x91\xff\xff\xff\xff\xc8")
- } else {
- for l := 0; l < Len; l++ {
- zbuf.WriteByte(byte(src.Intn(256)))
- }
- }
-
- buf.Reset()
- if err := huffmanDecode(&buf, 0, zbuf.Bytes()); err != nil {
- if err == ErrInvalidHuffman {
- numFail++
- continue
- }
- t.Errorf("Failed to decode %q: %v", zbuf.Bytes(), err)
- continue
- }
- }
- t.Logf("%0.02f%% are invalid (%d / %d)", 100*float64(numFail)/float64(n), numFail, n)
- if numFail < 1 {
- t.Error("expected at least one invalid huffman encoding (test starts with one)")
- }
-}
-
-func TestReadVarInt(t *testing.T) {
- type res struct {
- i uint64
- consumed int
- err error
- }
- tests := []struct {
- n byte
- p []byte
- want res
- }{
- // Fits in a byte:
- {1, []byte{0}, res{0, 1, nil}},
- {2, []byte{2}, res{2, 1, nil}},
- {3, []byte{6}, res{6, 1, nil}},
- {4, []byte{14}, res{14, 1, nil}},
- {5, []byte{30}, res{30, 1, nil}},
- {6, []byte{62}, res{62, 1, nil}},
- {7, []byte{126}, res{126, 1, nil}},
- {8, []byte{254}, res{254, 1, nil}},
-
- // Doesn't fit in a byte:
- {1, []byte{1}, res{0, 0, errNeedMore}},
- {2, []byte{3}, res{0, 0, errNeedMore}},
- {3, []byte{7}, res{0, 0, errNeedMore}},
- {4, []byte{15}, res{0, 0, errNeedMore}},
- {5, []byte{31}, res{0, 0, errNeedMore}},
- {6, []byte{63}, res{0, 0, errNeedMore}},
- {7, []byte{127}, res{0, 0, errNeedMore}},
- {8, []byte{255}, res{0, 0, errNeedMore}},
-
- // Ignoring top bits:
- {5, []byte{255, 154, 10}, res{1337, 3, nil}}, // high dummy three bits: 111
- {5, []byte{159, 154, 10}, res{1337, 3, nil}}, // high dummy three bits: 100
- {5, []byte{191, 154, 10}, res{1337, 3, nil}}, // high dummy three bits: 101
-
- // Extra byte:
- {5, []byte{191, 154, 10, 2}, res{1337, 3, nil}}, // extra byte
-
- // Short a byte:
- {5, []byte{191, 154}, res{0, 0, errNeedMore}},
-
- // integer overflow:
- {1, []byte{255, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128}, res{0, 0, errVarintOverflow}},
- }
- for _, tt := range tests {
- i, remain, err := readVarInt(tt.n, tt.p)
- consumed := len(tt.p) - len(remain)
- got := res{i, consumed, err}
- if got != tt.want {
- t.Errorf("readVarInt(%d, %v ~ %x) = %+v; want %+v", tt.n, tt.p, tt.p, got, tt.want)
- }
- }
-}
-
-// Fuzz crash, originally reported at https://github.com/bradfitz/http2/issues/56
-func TestHuffmanFuzzCrash(t *testing.T) {
- got, err := HuffmanDecodeToString([]byte("00\x91\xff\xff\xff\xff\xc8"))
- if got != "" {
- t.Errorf("Got %q; want empty string", got)
- }
- if err != ErrInvalidHuffman {
- t.Errorf("Err = %v; want ErrInvalidHuffman", err)
- }
-}
-
-func dehex(s string) []byte {
- s = strings.Replace(s, " ", "", -1)
- s = strings.Replace(s, "\n", "", -1)
- b, err := hex.DecodeString(s)
- if err != nil {
- panic(err)
- }
- return b
-}
-
-func TestEmitEnabled(t *testing.T) {
- var buf bytes.Buffer
- enc := NewEncoder(&buf)
- enc.WriteField(HeaderField{Name: "foo", Value: "bar"})
- enc.WriteField(HeaderField{Name: "foo", Value: "bar"})
-
- numCallback := 0
- var dec *Decoder
- dec = NewDecoder(8<<20, func(HeaderField) {
- numCallback++
- dec.SetEmitEnabled(false)
- })
- if !dec.EmitEnabled() {
- t.Errorf("initial emit enabled = false; want true")
- }
- if _, err := dec.Write(buf.Bytes()); err != nil {
- t.Error(err)
- }
- if numCallback != 1 {
- t.Errorf("num callbacks = %d; want 1", numCallback)
- }
- if dec.EmitEnabled() {
- t.Errorf("emit enabled = true; want false")
- }
-}
-
-func TestSaveBufLimit(t *testing.T) {
- const maxStr = 1 << 10
- var got []HeaderField
- dec := NewDecoder(initialHeaderTableSize, func(hf HeaderField) {
- got = append(got, hf)
- })
- dec.SetMaxStringLength(maxStr)
- var frag []byte
- frag = append(frag[:0], encodeTypeByte(false, false))
- frag = appendVarInt(frag, 7, 3)
- frag = append(frag, "foo"...)
- frag = appendVarInt(frag, 7, 3)
- frag = append(frag, "bar"...)
-
- if _, err := dec.Write(frag); err != nil {
- t.Fatal(err)
- }
-
- want := []HeaderField{{Name: "foo", Value: "bar"}}
- if !reflect.DeepEqual(got, want) {
- t.Errorf("After small writes, got %v; want %v", got, want)
- }
-
- frag = append(frag[:0], encodeTypeByte(false, false))
- frag = appendVarInt(frag, 7, maxStr*3)
- frag = append(frag, make([]byte, maxStr*3)...)
-
- _, err := dec.Write(frag)
- if err != ErrStringLength {
- t.Fatalf("Write error = %v; want ErrStringLength", err)
- }
-}
diff --git a/Godeps/_workspace/src/golang.org/x/net/http2/http2.go b/Godeps/_workspace/src/golang.org/x/net/http2/http2.go
index 3ed51a44e8c..4c5e11ac2cd 100644
--- a/Godeps/_workspace/src/golang.org/x/net/http2/http2.go
+++ b/Godeps/_workspace/src/golang.org/x/net/http2/http2.go
@@ -17,6 +17,7 @@ package http2
import (
"bufio"
+ "crypto/tls"
"errors"
"fmt"
"io"
@@ -422,3 +423,7 @@ var isTokenTable = [127]bool{
'|': true,
'~': true,
}
+
+type connectionStater interface {
+ ConnectionState() tls.ConnectionState
+}
diff --git a/Godeps/_workspace/src/golang.org/x/net/http2/http2_test.go b/Godeps/_workspace/src/golang.org/x/net/http2/http2_test.go
deleted file mode 100644
index d32e0667d58..00000000000
--- a/Godeps/_workspace/src/golang.org/x/net/http2/http2_test.go
+++ /dev/null
@@ -1,174 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package http2
-
-import (
- "bytes"
- "errors"
- "flag"
- "fmt"
- "net/http"
- "os/exec"
- "strconv"
- "strings"
- "testing"
-
- "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/http2/hpack"
-)
-
-var knownFailing = flag.Bool("known_failing", false, "Run known-failing tests.")
-
-func condSkipFailingTest(t *testing.T) {
- if !*knownFailing {
- t.Skip("Skipping known-failing test without --known_failing")
- }
-}
-
-func init() {
- DebugGoroutines = true
- flag.BoolVar(&VerboseLogs, "verboseh2", false, "Verbose HTTP/2 debug logging")
-}
-
-func TestSettingString(t *testing.T) {
- tests := []struct {
- s Setting
- want string
- }{
- {Setting{SettingMaxFrameSize, 123}, "[MAX_FRAME_SIZE = 123]"},
- {Setting{1<<16 - 1, 123}, "[UNKNOWN_SETTING_65535 = 123]"},
- }
- for i, tt := range tests {
- got := fmt.Sprint(tt.s)
- if got != tt.want {
- t.Errorf("%d. for %#v, string = %q; want %q", i, tt.s, got, tt.want)
- }
- }
-}
-
-type twriter struct {
- t testing.TB
- st *serverTester // optional
-}
-
-func (w twriter) Write(p []byte) (n int, err error) {
- if w.st != nil {
- ps := string(p)
- for _, phrase := range w.st.logFilter {
- if strings.Contains(ps, phrase) {
- return len(p), nil // no logging
- }
- }
- }
- w.t.Logf("%s", p)
- return len(p), nil
-}
-
-// like encodeHeader, but don't add implicit psuedo headers.
-func encodeHeaderNoImplicit(t *testing.T, headers ...string) []byte {
- var buf bytes.Buffer
- enc := hpack.NewEncoder(&buf)
- for len(headers) > 0 {
- k, v := headers[0], headers[1]
- headers = headers[2:]
- if err := enc.WriteField(hpack.HeaderField{Name: k, Value: v}); err != nil {
- t.Fatalf("HPACK encoding error for %q/%q: %v", k, v, err)
- }
- }
- return buf.Bytes()
-}
-
-// Verify that curl has http2.
-func requireCurl(t *testing.T) {
- out, err := dockerLogs(curl(t, "--version"))
- if err != nil {
- t.Skipf("failed to determine curl features; skipping test")
- }
- if !strings.Contains(string(out), "HTTP2") {
- t.Skip("curl doesn't support HTTP2; skipping test")
- }
-}
-
-func curl(t *testing.T, args ...string) (container string) {
- out, err := exec.Command("docker", append([]string{"run", "-d", "--net=host", "gohttp2/curl"}, args...)...).Output()
- if err != nil {
- t.Skipf("Failed to run curl in docker: %v, %s", err, out)
- }
- return strings.TrimSpace(string(out))
-}
-
-// Verify that h2load exists.
-func requireH2load(t *testing.T) {
- out, err := dockerLogs(h2load(t, "--version"))
- if err != nil {
- t.Skipf("failed to probe h2load; skipping test: %s", out)
- }
- if !strings.Contains(string(out), "h2load nghttp2/") {
- t.Skipf("h2load not present; skipping test. (Output=%q)", out)
- }
-}
-
-func h2load(t *testing.T, args ...string) (container string) {
- out, err := exec.Command("docker", append([]string{"run", "-d", "--net=host", "--entrypoint=/usr/local/bin/h2load", "gohttp2/curl"}, args...)...).Output()
- if err != nil {
- t.Skipf("Failed to run h2load in docker: %v, %s", err, out)
- }
- return strings.TrimSpace(string(out))
-}
-
-type puppetCommand struct {
- fn func(w http.ResponseWriter, r *http.Request)
- done chan<- bool
-}
-
-type handlerPuppet struct {
- ch chan puppetCommand
-}
-
-func newHandlerPuppet() *handlerPuppet {
- return &handlerPuppet{
- ch: make(chan puppetCommand),
- }
-}
-
-func (p *handlerPuppet) act(w http.ResponseWriter, r *http.Request) {
- for cmd := range p.ch {
- cmd.fn(w, r)
- cmd.done <- true
- }
-}
-
-func (p *handlerPuppet) done() { close(p.ch) }
-func (p *handlerPuppet) do(fn func(http.ResponseWriter, *http.Request)) {
- done := make(chan bool)
- p.ch <- puppetCommand{fn, done}
- <-done
-}
-func dockerLogs(container string) ([]byte, error) {
- out, err := exec.Command("docker", "wait", container).CombinedOutput()
- if err != nil {
- return out, err
- }
- exitStatus, err := strconv.Atoi(strings.TrimSpace(string(out)))
- if err != nil {
- return out, errors.New("unexpected exit status from docker wait")
- }
- out, err = exec.Command("docker", "logs", container).CombinedOutput()
- exec.Command("docker", "rm", container).Run()
- if err == nil && exitStatus != 0 {
- err = fmt.Errorf("exit status %d: %s", exitStatus, out)
- }
- return out, err
-}
-
-func kill(container string) {
- exec.Command("docker", "kill", container).Run()
- exec.Command("docker", "rm", container).Run()
-}
-
-func cleanDate(res *http.Response) {
- if d := res.Header["Date"]; len(d) == 1 {
- d[0] = "XXX"
- }
-}
diff --git a/Godeps/_workspace/src/golang.org/x/net/http2/pipe_test.go b/Godeps/_workspace/src/golang.org/x/net/http2/pipe_test.go
deleted file mode 100644
index 76322999965..00000000000
--- a/Godeps/_workspace/src/golang.org/x/net/http2/pipe_test.go
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package http2
-
-import (
- "bytes"
- "errors"
- "io"
- "io/ioutil"
- "testing"
-)
-
-func TestPipeClose(t *testing.T) {
- var p pipe
- p.b = new(bytes.Buffer)
- a := errors.New("a")
- b := errors.New("b")
- p.CloseWithError(a)
- p.CloseWithError(b)
- _, err := p.Read(make([]byte, 1))
- if err != a {
- t.Errorf("err = %v want %v", err, a)
- }
-}
-
-func TestPipeDoneChan(t *testing.T) {
- var p pipe
- done := p.Done()
- select {
- case <-done:
- t.Fatal("done too soon")
- default:
- }
- p.CloseWithError(io.EOF)
- select {
- case <-done:
- default:
- t.Fatal("should be done")
- }
-}
-
-func TestPipeDoneChan_ErrFirst(t *testing.T) {
- var p pipe
- p.CloseWithError(io.EOF)
- done := p.Done()
- select {
- case <-done:
- default:
- t.Fatal("should be done")
- }
-}
-
-func TestPipeDoneChan_Break(t *testing.T) {
- var p pipe
- done := p.Done()
- select {
- case <-done:
- t.Fatal("done too soon")
- default:
- }
- p.BreakWithError(io.EOF)
- select {
- case <-done:
- default:
- t.Fatal("should be done")
- }
-}
-
-func TestPipeDoneChan_Break_ErrFirst(t *testing.T) {
- var p pipe
- p.BreakWithError(io.EOF)
- done := p.Done()
- select {
- case <-done:
- default:
- t.Fatal("should be done")
- }
-}
-
-func TestPipeCloseWithError(t *testing.T) {
- p := &pipe{b: new(bytes.Buffer)}
- const body = "foo"
- io.WriteString(p, body)
- a := errors.New("test error")
- p.CloseWithError(a)
- all, err := ioutil.ReadAll(p)
- if string(all) != body {
- t.Errorf("read bytes = %q; want %q", all, body)
- }
- if err != a {
- t.Logf("read error = %v, %v", err, a)
- }
-}
-
-func TestPipeBreakWithError(t *testing.T) {
- p := &pipe{b: new(bytes.Buffer)}
- io.WriteString(p, "foo")
- a := errors.New("test err")
- p.BreakWithError(a)
- all, err := ioutil.ReadAll(p)
- if string(all) != "" {
- t.Errorf("read bytes = %q; want empty string", all)
- }
- if err != a {
- t.Logf("read error = %v, %v", err, a)
- }
-}
diff --git a/Godeps/_workspace/src/golang.org/x/net/http2/priority_test.go b/Godeps/_workspace/src/golang.org/x/net/http2/priority_test.go
deleted file mode 100644
index a3fe2bb4901..00000000000
--- a/Godeps/_workspace/src/golang.org/x/net/http2/priority_test.go
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package http2
-
-import (
- "testing"
-)
-
-func TestPriority(t *testing.T) {
- // A -> B
- // move A's parent to B
- streams := make(map[uint32]*stream)
- a := &stream{
- parent: nil,
- weight: 16,
- }
- streams[1] = a
- b := &stream{
- parent: a,
- weight: 16,
- }
- streams[2] = b
- adjustStreamPriority(streams, 1, PriorityParam{
- Weight: 20,
- StreamDep: 2,
- })
- if a.parent != b {
- t.Errorf("Expected A's parent to be B")
- }
- if a.weight != 20 {
- t.Errorf("Expected A's weight to be 20; got %d", a.weight)
- }
- if b.parent != nil {
- t.Errorf("Expected B to have no parent")
- }
- if b.weight != 16 {
- t.Errorf("Expected B's weight to be 16; got %d", b.weight)
- }
-}
-
-func TestPriorityExclusiveZero(t *testing.T) {
- // A B and C are all children of the 0 stream.
- // Exclusive reprioritization to any of the streams
- // should bring the rest of the streams under the
- // reprioritized stream
- streams := make(map[uint32]*stream)
- a := &stream{
- parent: nil,
- weight: 16,
- }
- streams[1] = a
- b := &stream{
- parent: nil,
- weight: 16,
- }
- streams[2] = b
- c := &stream{
- parent: nil,
- weight: 16,
- }
- streams[3] = c
- adjustStreamPriority(streams, 3, PriorityParam{
- Weight: 20,
- StreamDep: 0,
- Exclusive: true,
- })
- if a.parent != c {
- t.Errorf("Expected A's parent to be C")
- }
- if a.weight != 16 {
- t.Errorf("Expected A's weight to be 16; got %d", a.weight)
- }
- if b.parent != c {
- t.Errorf("Expected B's parent to be C")
- }
- if b.weight != 16 {
- t.Errorf("Expected B's weight to be 16; got %d", b.weight)
- }
- if c.parent != nil {
- t.Errorf("Expected C to have no parent")
- }
- if c.weight != 20 {
- t.Errorf("Expected C's weight to be 20; got %d", b.weight)
- }
-}
-
-func TestPriorityOwnParent(t *testing.T) {
- streams := make(map[uint32]*stream)
- a := &stream{
- parent: nil,
- weight: 16,
- }
- streams[1] = a
- b := &stream{
- parent: a,
- weight: 16,
- }
- streams[2] = b
- adjustStreamPriority(streams, 1, PriorityParam{
- Weight: 20,
- StreamDep: 1,
- })
- if a.parent != nil {
- t.Errorf("Expected A's parent to be nil")
- }
- if a.weight != 20 {
- t.Errorf("Expected A's weight to be 20; got %d", a.weight)
- }
- if b.parent != a {
- t.Errorf("Expected B's parent to be A")
- }
- if b.weight != 16 {
- t.Errorf("Expected B's weight to be 16; got %d", b.weight)
- }
-
-}
diff --git a/Godeps/_workspace/src/golang.org/x/net/http2/server.go b/Godeps/_workspace/src/golang.org/x/net/http2/server.go
index e3f3567dd1d..f652911bf80 100644
--- a/Godeps/_workspace/src/golang.org/x/net/http2/server.go
+++ b/Godeps/_workspace/src/golang.org/x/net/http2/server.go
@@ -51,6 +51,7 @@ import (
"os"
"reflect"
"runtime"
+ "sort"
"strconv"
"strings"
"sync"
@@ -194,28 +195,76 @@ func ConfigureServer(s *http.Server, conf *Server) error {
if testHookOnConn != nil {
testHookOnConn()
}
- conf.handleConn(hs, c, h)
+ conf.ServeConn(c, &ServeConnOpts{
+ Handler: h,
+ BaseConfig: hs,
+ })
}
s.TLSNextProto[NextProtoTLS] = protoHandler
s.TLSNextProto["h2-14"] = protoHandler // temporary; see above.
return nil
}
-func (srv *Server) handleConn(hs *http.Server, c net.Conn, h http.Handler) {
+// ServeConnOpts are options for the Server.ServeConn method.
+type ServeConnOpts struct {
+ // BaseConfig optionally sets the base configuration
+ // for values. If nil, defaults are used.
+ BaseConfig *http.Server
+
+ // Handler specifies which handler to use for processing
+ // requests. If nil, BaseConfig.Handler is used. If BaseConfig
+ // or BaseConfig.Handler is nil, http.DefaultServeMux is used.
+ Handler http.Handler
+}
+
+func (o *ServeConnOpts) baseConfig() *http.Server {
+ if o != nil && o.BaseConfig != nil {
+ return o.BaseConfig
+ }
+ return new(http.Server)
+}
+
+func (o *ServeConnOpts) handler() http.Handler {
+ if o != nil {
+ if o.Handler != nil {
+ return o.Handler
+ }
+ if o.BaseConfig != nil && o.BaseConfig.Handler != nil {
+ return o.BaseConfig.Handler
+ }
+ }
+ return http.DefaultServeMux
+}
+
+// ServeConn serves HTTP/2 requests on the provided connection and
+// blocks until the connection is no longer readable.
+//
+// ServeConn starts speaking HTTP/2 assuming that c has not had any
+// reads or writes. It writes its initial settings frame and expects
+// to be able to read the preface and settings frame from the
+// client. If c has a ConnectionState method like a *tls.Conn, the
+// ConnectionState is used to verify the TLS ciphersuite and to set
+// the Request.TLS field in Handlers.
+//
+// ServeConn does not support h2c by itself. Any h2c support must be
+// implemented in terms of providing a suitably-behaving net.Conn.
+//
+// The opts parameter is optional. If nil, default values are used.
+func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
sc := &serverConn{
- srv: srv,
- hs: hs,
+ srv: s,
+ hs: opts.baseConfig(),
conn: c,
remoteAddrStr: c.RemoteAddr().String(),
bw: newBufferedWriter(c),
- handler: h,
+ handler: opts.handler(),
streams: make(map[uint32]*stream),
readFrameCh: make(chan readFrameResult),
wantWriteFrameCh: make(chan frameWriteMsg, 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{}),
- advMaxStreams: srv.maxConcurrentStreams(),
+ advMaxStreams: s.maxConcurrentStreams(),
writeSched: writeScheduler{
maxFrameSize: initialMaxFrameSize,
},
@@ -227,14 +276,14 @@ func (srv *Server) handleConn(hs *http.Server, c net.Conn, h http.Handler) {
sc.flow.add(initialWindowSize)
sc.inflow.add(initialWindowSize)
sc.hpackEncoder = hpack.NewEncoder(&sc.headerWriteBuf)
- sc.hpackDecoder = hpack.NewDecoder(initialHeaderTableSize, nil)
- sc.hpackDecoder.SetMaxStringLength(sc.maxHeaderStringLen())
fr := NewFramer(sc.bw, c)
- fr.SetMaxReadFrameSize(srv.maxReadFrameSize())
+ fr.ReadMetaHeaders = hpack.NewDecoder(initialHeaderTableSize, nil)
+ fr.MaxHeaderListSize = sc.maxHeaderListSize()
+ fr.SetMaxReadFrameSize(s.maxReadFrameSize())
sc.framer = fr
- if tc, ok := c.(*tls.Conn); ok {
+ if tc, ok := c.(connectionStater); ok {
sc.tlsState = new(tls.ConnectionState)
*sc.tlsState = tc.ConnectionState()
// 9.2 Use of TLS Features
@@ -264,7 +313,7 @@ func (srv *Server) handleConn(hs *http.Server, c net.Conn, h http.Handler) {
// So for now, do nothing here again.
}
- if !srv.PermitProhibitedCipherSuites && isBadCipher(sc.tlsState.CipherSuite) {
+ if !s.PermitProhibitedCipherSuites && isBadCipher(sc.tlsState.CipherSuite) {
// "Endpoints MAY choose to generate a connection error
// (Section 5.4.1) of type INADEQUATE_SECURITY if one of
// the prohibited cipher suites are negotiated."
@@ -326,7 +375,6 @@ type serverConn struct {
bw *bufferedWriter // writing to conn
handler http.Handler
framer *Framer
- hpackDecoder *hpack.Decoder
doneServing chan struct{} // closed when serverConn.serve ends
readFrameCh chan readFrameResult // written by serverConn.readFrames
wantWriteFrameCh chan frameWriteMsg // from handlers -> serve
@@ -353,7 +401,6 @@ type serverConn struct {
headerTableSize uint32
peerMaxHeaderListSize uint32 // zero means unknown (default)
canonHeader map[string]string // http2-lower-case -> Go-Canonical-Case
- req requestParam // non-zero while reading request headers
writingFrame bool // started write goroutine but haven't heard back on wroteFrameCh
needsFrameFlush bool // last frame write wasn't a flush
writeSched writeScheduler
@@ -368,16 +415,6 @@ type serverConn struct {
hpackEncoder *hpack.Encoder
}
-func (sc *serverConn) maxHeaderStringLen() int {
- v := sc.maxHeaderListSize()
- if uint32(int(v)) == v {
- return int(v)
- }
- // They had a crazy big number for MaxHeaderBytes anyway,
- // so give them unlimited header lengths:
- return 0
-}
-
func (sc *serverConn) maxHeaderListSize() uint32 {
n := sc.hs.MaxHeaderBytes
if n <= 0 {
@@ -390,21 +427,6 @@ func (sc *serverConn) maxHeaderListSize() uint32 {
return uint32(n + typicalHeaders*perFieldOverhead)
}
-// requestParam is the state of the next request, initialized over
-// potentially several frames HEADERS + zero or more CONTINUATION
-// frames.
-type requestParam struct {
- // stream is non-nil if we're reading (HEADER or CONTINUATION)
- // frames for a request (but not DATA).
- stream *stream
- header http.Header
- method, path string
- scheme, authority string
- sawRegularHeader bool // saw a non-pseudo header already
- invalidHeader bool // an invalid header was seen
- headerListSize int64 // actually uint32, but easier math this way
-}
-
// stream represents a stream. This is the minimal metadata needed by
// the serve goroutine. Most of the actual stream state is owned by
// the http.Handler's goroutine in the responseWriter. Because the
@@ -540,87 +562,6 @@ func (sc *serverConn) condlogf(err error, format string, args ...interface{}) {
}
}
-func (sc *serverConn) onNewHeaderField(f hpack.HeaderField) {
- sc.serveG.check()
- if VerboseLogs {
- sc.vlogf("http2: server decoded %v", f)
- }
- switch {
- case !validHeaderFieldValue(f.Value): // f.Name checked _after_ pseudo check, since ':' is invalid
- sc.req.invalidHeader = true
- case strings.HasPrefix(f.Name, ":"):
- if sc.req.sawRegularHeader {
- sc.logf("pseudo-header after regular header")
- sc.req.invalidHeader = true
- return
- }
- var dst *string
- switch f.Name {
- case ":method":
- dst = &sc.req.method
- case ":path":
- dst = &sc.req.path
- case ":scheme":
- dst = &sc.req.scheme
- case ":authority":
- dst = &sc.req.authority
- default:
- // 8.1.2.1 Pseudo-Header Fields
- // "Endpoints MUST treat a request or response
- // that contains undefined or invalid
- // pseudo-header fields as malformed (Section
- // 8.1.2.6)."
- sc.logf("invalid pseudo-header %q", f.Name)
- sc.req.invalidHeader = true
- return
- }
- if *dst != "" {
- sc.logf("duplicate pseudo-header %q sent", f.Name)
- sc.req.invalidHeader = true
- return
- }
- *dst = f.Value
- case !validHeaderFieldName(f.Name):
- sc.req.invalidHeader = true
- default:
- sc.req.sawRegularHeader = true
- sc.req.header.Add(sc.canonicalHeader(f.Name), f.Value)
- const headerFieldOverhead = 32 // per spec
- sc.req.headerListSize += int64(len(f.Name)) + int64(len(f.Value)) + headerFieldOverhead
- if sc.req.headerListSize > int64(sc.maxHeaderListSize()) {
- sc.hpackDecoder.SetEmitEnabled(false)
- }
- }
-}
-
-func (st *stream) onNewTrailerField(f hpack.HeaderField) {
- sc := st.sc
- sc.serveG.check()
- if VerboseLogs {
- sc.vlogf("http2: server decoded trailer %v", f)
- }
- switch {
- case strings.HasPrefix(f.Name, ":"):
- sc.req.invalidHeader = true
- return
- case !validHeaderFieldName(f.Name) || !validHeaderFieldValue(f.Value):
- sc.req.invalidHeader = true
- return
- default:
- key := sc.canonicalHeader(f.Name)
- if st.trailer != nil {
- vv := append(st.trailer[key], f.Value)
- st.trailer[key] = vv
-
- // arbitrary; TODO: read spec about header list size limits wrt trailers
- const tooBig = 1000
- if len(vv) >= tooBig {
- sc.hpackDecoder.SetEmitEnabled(false)
- }
- }
- }
-}
-
func (sc *serverConn) canonicalHeader(v string) string {
sc.serveG.check()
cv, ok := commonCanonHeader[v]
@@ -1134,10 +1075,8 @@ func (sc *serverConn) processFrame(f Frame) error {
switch f := f.(type) {
case *SettingsFrame:
return sc.processSettings(f)
- case *HeadersFrame:
+ case *MetaHeadersFrame:
return sc.processHeaders(f)
- case *ContinuationFrame:
- return sc.processContinuation(f)
case *WindowUpdateFrame:
return sc.processWindowUpdate(f)
case *PingFrame:
@@ -1393,7 +1332,7 @@ func (st *stream) copyTrailersToHandlerRequest() {
}
}
-func (sc *serverConn) processHeaders(f *HeadersFrame) error {
+func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
sc.serveG.check()
id := f.Header().StreamID
if sc.inGoAway {
@@ -1422,13 +1361,11 @@ func (sc *serverConn) processHeaders(f *HeadersFrame) error {
// endpoint has opened or reserved. [...] An endpoint that
// receives an unexpected stream identifier MUST respond with
// a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
- if id <= sc.maxStreamID || sc.req.stream != nil {
+ if id <= sc.maxStreamID {
return ConnectionError(ErrCodeProtocol)
}
+ sc.maxStreamID = id
- if id > sc.maxStreamID {
- sc.maxStreamID = id
- }
st = &stream{
sc: sc,
id: id,
@@ -1452,50 +1389,6 @@ func (sc *serverConn) processHeaders(f *HeadersFrame) error {
if sc.curOpenStreams == 1 {
sc.setConnState(http.StateActive)
}
- sc.req = requestParam{
- stream: st,
- header: make(http.Header),
- }
- sc.hpackDecoder.SetEmitFunc(sc.onNewHeaderField)
- sc.hpackDecoder.SetEmitEnabled(true)
- return sc.processHeaderBlockFragment(st, f.HeaderBlockFragment(), f.HeadersEnded())
-}
-
-func (st *stream) processTrailerHeaders(f *HeadersFrame) error {
- sc := st.sc
- sc.serveG.check()
- if st.gotTrailerHeader {
- return ConnectionError(ErrCodeProtocol)
- }
- st.gotTrailerHeader = true
- if !f.StreamEnded() {
- return StreamError{st.id, ErrCodeProtocol}
- }
- sc.resetPendingRequest() // we use invalidHeader from it for trailers
- return st.processTrailerHeaderBlockFragment(f.HeaderBlockFragment(), f.HeadersEnded())
-}
-
-func (sc *serverConn) processContinuation(f *ContinuationFrame) error {
- sc.serveG.check()
- st := sc.streams[f.Header().StreamID]
- if st.gotTrailerHeader {
- return st.processTrailerHeaderBlockFragment(f.HeaderBlockFragment(), f.HeadersEnded())
- }
- return sc.processHeaderBlockFragment(st, f.HeaderBlockFragment(), f.HeadersEnded())
-}
-
-func (sc *serverConn) processHeaderBlockFragment(st *stream, frag []byte, end bool) error {
- sc.serveG.check()
- if _, err := sc.hpackDecoder.Write(frag); err != nil {
- return ConnectionError(ErrCodeCompression)
- }
- if !end {
- return nil
- }
- if err := sc.hpackDecoder.Close(); err != nil {
- return ConnectionError(ErrCodeCompression)
- }
- defer sc.resetPendingRequest()
if sc.curOpenStreams > sc.advMaxStreams {
// "Endpoints MUST NOT exceed the limit set by their
// peer. An endpoint that receives a HEADERS frame
@@ -1515,7 +1408,7 @@ func (sc *serverConn) processHeaderBlockFragment(st *stream, frag []byte, end bo
return StreamError{st.id, ErrCodeRefusedStream}
}
- rw, req, err := sc.newWriterAndRequest()
+ rw, req, err := sc.newWriterAndRequest(st, f)
if err != nil {
return err
}
@@ -1527,7 +1420,7 @@ func (sc *serverConn) processHeaderBlockFragment(st *stream, frag []byte, end bo
st.declBodyBytes = req.ContentLength
handler := sc.handler.ServeHTTP
- if !sc.hpackDecoder.EmitEnabled() {
+ if f.Truncated {
// Their header list was too long. Send a 431 error.
handler = handleHeaderListTooLong
}
@@ -1536,27 +1429,27 @@ func (sc *serverConn) processHeaderBlockFragment(st *stream, frag []byte, end bo
return nil
}
-func (st *stream) processTrailerHeaderBlockFragment(frag []byte, end bool) error {
+func (st *stream) processTrailerHeaders(f *MetaHeadersFrame) error {
sc := st.sc
sc.serveG.check()
- sc.hpackDecoder.SetEmitFunc(st.onNewTrailerField)
- if _, err := sc.hpackDecoder.Write(frag); err != nil {
- return ConnectionError(ErrCodeCompression)
+ if st.gotTrailerHeader {
+ return ConnectionError(ErrCodeProtocol)
}
- if !end {
- return nil
+ st.gotTrailerHeader = true
+ if !f.StreamEnded() {
+ return StreamError{st.id, ErrCodeProtocol}
}
- rp := &sc.req
- if rp.invalidHeader {
- return StreamError{rp.stream.id, ErrCodeProtocol}
+ if len(f.PseudoFields()) > 0 {
+ return StreamError{st.id, ErrCodeProtocol}
}
-
- err := sc.hpackDecoder.Close()
- st.endStream()
- if err != nil {
- return ConnectionError(ErrCodeCompression)
+ if st.trailer != nil {
+ for _, hf := range f.RegularFields() {
+ key := sc.canonicalHeader(hf.Name)
+ st.trailer[key] = append(st.trailer[key], hf.Value)
+ }
}
+ st.endStream()
return nil
}
@@ -1601,29 +1494,21 @@ func adjustStreamPriority(streams map[uint32]*stream, streamID uint32, priority
}
}
-// resetPendingRequest zeros out all state related to a HEADERS frame
-// and its zero or more CONTINUATION frames sent to start a new
-// request.
-func (sc *serverConn) resetPendingRequest() {
- sc.serveG.check()
- sc.req = requestParam{}
-}
-
-func (sc *serverConn) newWriterAndRequest() (*responseWriter, *http.Request, error) {
+func (sc *serverConn) newWriterAndRequest(st *stream, f *MetaHeadersFrame) (*responseWriter, *http.Request, error) {
sc.serveG.check()
- rp := &sc.req
- if rp.invalidHeader {
- return nil, nil, StreamError{rp.stream.id, ErrCodeProtocol}
- }
+ method := f.PseudoValue("method")
+ path := f.PseudoValue("path")
+ scheme := f.PseudoValue("scheme")
+ authority := f.PseudoValue("authority")
- isConnect := rp.method == "CONNECT"
+ isConnect := method == "CONNECT"
if isConnect {
- if rp.path != "" || rp.scheme != "" || rp.authority == "" {
- return nil, nil, StreamError{rp.stream.id, ErrCodeProtocol}
+ if path != "" || scheme != "" || authority == "" {
+ return nil, nil, StreamError{f.StreamID, ErrCodeProtocol}
}
- } else if rp.method == "" || rp.path == "" ||
- (rp.scheme != "https" && rp.scheme != "http") {
+ } else if method == "" || path == "" ||
+ (scheme != "https" && scheme != "http") {
// See 8.1.2.6 Malformed Requests and Responses:
//
// Malformed requests or responses that are detected
@@ -1634,35 +1519,40 @@ func (sc *serverConn) newWriterAndRequest() (*responseWriter, *http.Request, err
// "All HTTP/2 requests MUST include exactly one valid
// value for the :method, :scheme, and :path
// pseudo-header fields"
- return nil, nil, StreamError{rp.stream.id, ErrCodeProtocol}
+ return nil, nil, StreamError{f.StreamID, ErrCodeProtocol}
}
- bodyOpen := rp.stream.state == stateOpen
- if rp.method == "HEAD" && bodyOpen {
+ bodyOpen := !f.StreamEnded()
+ if method == "HEAD" && bodyOpen {
// HEAD requests can't have bodies
- return nil, nil, StreamError{rp.stream.id, ErrCodeProtocol}
+ return nil, nil, StreamError{f.StreamID, ErrCodeProtocol}
}
var tlsState *tls.ConnectionState // nil if not scheme https
- if rp.scheme == "https" {
+ if scheme == "https" {
tlsState = sc.tlsState
}
- authority := rp.authority
+
+ header := make(http.Header)
+ for _, hf := range f.RegularFields() {
+ header.Add(sc.canonicalHeader(hf.Name), hf.Value)
+ }
+
if authority == "" {
- authority = rp.header.Get("Host")
+ authority = header.Get("Host")
}
- needsContinue := rp.header.Get("Expect") == "100-continue"
+ needsContinue := header.Get("Expect") == "100-continue"
if needsContinue {
- rp.header.Del("Expect")
+ header.Del("Expect")
}
// Merge Cookie headers into one "; "-delimited value.
- if cookies := rp.header["Cookie"]; len(cookies) > 1 {
- rp.header.Set("Cookie", strings.Join(cookies, "; "))
+ if cookies := header["Cookie"]; len(cookies) > 1 {
+ header.Set("Cookie", strings.Join(cookies, "; "))
}
// Setup Trailers
var trailer http.Header
- for _, v := range rp.header["Trailer"] {
+ for _, v := range header["Trailer"] {
for _, key := range strings.Split(v, ",") {
key = http.CanonicalHeaderKey(strings.TrimSpace(key))
switch key {
@@ -1677,31 +1567,31 @@ func (sc *serverConn) newWriterAndRequest() (*responseWriter, *http.Request, err
}
}
}
- delete(rp.header, "Trailer")
+ delete(header, "Trailer")
body := &requestBody{
conn: sc,
- stream: rp.stream,
+ stream: st,
needsContinue: needsContinue,
}
var url_ *url.URL
var requestURI string
if isConnect {
- url_ = &url.URL{Host: rp.authority}
- requestURI = rp.authority // mimic HTTP/1 server behavior
+ url_ = &url.URL{Host: authority}
+ requestURI = authority // mimic HTTP/1 server behavior
} else {
var err error
- url_, err = url.ParseRequestURI(rp.path)
+ url_, err = url.ParseRequestURI(path)
if err != nil {
- return nil, nil, StreamError{rp.stream.id, ErrCodeProtocol}
+ return nil, nil, StreamError{f.StreamID, ErrCodeProtocol}
}
- requestURI = rp.path
+ requestURI = path
}
req := &http.Request{
- Method: rp.method,
+ Method: method,
URL: url_,
RemoteAddr: sc.remoteAddrStr,
- Header: rp.header,
+ Header: header,
RequestURI: requestURI,
Proto: "HTTP/2.0",
ProtoMajor: 2,
@@ -1716,7 +1606,7 @@ func (sc *serverConn) newWriterAndRequest() (*responseWriter, *http.Request, err
b: &fixedBuffer{buf: make([]byte, initialWindowSize)}, // TODO: garbage
}
- if vv, ok := rp.header["Content-Length"]; ok {
+ if vv, ok := header["Content-Length"]; ok {
req.ContentLength, _ = strconv.ParseInt(vv[0], 10, 64)
} else {
req.ContentLength = -1
@@ -1729,7 +1619,7 @@ func (sc *serverConn) newWriterAndRequest() (*responseWriter, *http.Request, err
rws.conn = sc
rws.bw = bwSave
rws.bw.Reset(chunkWriter{rws})
- rws.stream = rp.stream
+ rws.stream = st
rws.req = req
rws.body = body
@@ -1973,7 +1863,9 @@ func (rws *responseWriterState) declareTrailer(k string) {
// Forbidden by RFC 2616 14.40.
return
}
- rws.trailers = append(rws.trailers, k)
+ if !strSliceContains(rws.trailers, k) {
+ rws.trailers = append(rws.trailers, k)
+ }
}
// writeChunk writes chunks from the bufio.Writer. But because
@@ -2041,6 +1933,10 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
return 0, nil
}
+ if rws.handlerDone {
+ rws.promoteUndeclaredTrailers()
+ }
+
endStream := rws.handlerDone && !rws.hasTrailers()
if len(p) > 0 || endStream {
// only send a 0 byte DATA frame if we're ending the stream.
@@ -2061,6 +1957,53 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
return len(p), nil
}
+// TrailerPrefix is a magic prefix for ResponseWriter.Header map keys
+// that, if present, signals that the map entry is actually for
+// the response trailers, and not the response headers. The prefix
+// is stripped after the ServeHTTP call finishes and the values are
+// sent in the trailers.
+//
+// This mechanism is intended only for trailers that are not known
+// prior to the headers being written. If the set of trailers is fixed
+// or known before the header is written, the normal Go trailers mechanism
+// is preferred:
+// https://golang.org/pkg/net/http/#ResponseWriter
+// https://golang.org/pkg/net/http/#example_ResponseWriter_trailers
+const TrailerPrefix = "Trailer:"
+
+// promoteUndeclaredTrailers permits http.Handlers to set trailers
+// after the header has already been flushed. Because the Go
+// ResponseWriter interface has no way to set Trailers (only the
+// Header), and because we didn't want to expand the ResponseWriter
+// interface, and because nobody used trailers, and because RFC 2616
+// says you SHOULD (but not must) predeclare any trailers in the
+// header, the official ResponseWriter rules said trailers in Go must
+// be predeclared, and then we reuse the same ResponseWriter.Header()
+// map to mean both Headers and Trailers. When it's time to write the
+// Trailers, we pick out the fields of Headers that were declared as
+// trailers. That worked for a while, until we found the first major
+// user of Trailers in the wild: gRPC (using them only over http2),
+// and gRPC libraries permit setting trailers mid-stream without
+// predeclarnig them. So: change of plans. We still permit the old
+// way, but we also permit this hack: if a Header() key begins with
+// "Trailer:", the suffix of that key is a Trailer. Because ':' is an
+// invalid token byte anyway, there is no ambiguity. (And it's already
+// filtered out) It's mildly hacky, but not terrible.
+//
+// This method runs after the Handler is done and promotes any Header
+// fields to be trailers.
+func (rws *responseWriterState) promoteUndeclaredTrailers() {
+ for k, vv := range rws.handlerHeader {
+ if !strings.HasPrefix(k, TrailerPrefix) {
+ continue
+ }
+ trailerKey := strings.TrimPrefix(k, TrailerPrefix)
+ rws.declareTrailer(trailerKey)
+ rws.handlerHeader[http.CanonicalHeaderKey(trailerKey)] = vv
+ }
+ sort.Strings(rws.trailers)
+}
+
func (w *responseWriter) Flush() {
rws := w.rws
if rws == nil {
diff --git a/Godeps/_workspace/src/golang.org/x/net/http2/server_test.go b/Godeps/_workspace/src/golang.org/x/net/http2/server_test.go
deleted file mode 100644
index 7ccaef59e4e..00000000000
--- a/Godeps/_workspace/src/golang.org/x/net/http2/server_test.go
+++ /dev/null
@@ -1,2940 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package http2
-
-import (
- "bytes"
- "crypto/tls"
- "errors"
- "flag"
- "fmt"
- "io"
- "io/ioutil"
- "log"
- "net"
- "net/http"
- "net/http/httptest"
- "os"
- "os/exec"
- "reflect"
- "runtime"
- "strconv"
- "strings"
- "sync"
- "sync/atomic"
- "testing"
- "time"
-
- "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/http2/hpack"
-)
-
-var stderrVerbose = flag.Bool("stderr_verbose", false, "Mirror verbosity to stderr, unbuffered")
-
-func stderrv() io.Writer {
- if *stderrVerbose {
- return os.Stderr
- }
-
- return ioutil.Discard
-}
-
-type serverTester struct {
- cc net.Conn // client conn
- t testing.TB
- ts *httptest.Server
- fr *Framer
- logBuf *bytes.Buffer
- logFilter []string // substrings to filter out
- scMu sync.Mutex // guards sc
- sc *serverConn
- hpackDec *hpack.Decoder
- decodedHeaders [][2]string
-
- // writing headers:
- headerBuf bytes.Buffer
- hpackEnc *hpack.Encoder
-
- // reading frames:
- frc chan Frame
- frErrc chan error
- readTimer *time.Timer
-}
-
-func init() {
- testHookOnPanicMu = new(sync.Mutex)
-}
-
-func resetHooks() {
- testHookOnPanicMu.Lock()
- testHookOnPanic = nil
- testHookOnPanicMu.Unlock()
-}
-
-type serverTesterOpt string
-
-var optOnlyServer = serverTesterOpt("only_server")
-var optQuiet = serverTesterOpt("quiet_logging")
-
-func newServerTester(t testing.TB, handler http.HandlerFunc, opts ...interface{}) *serverTester {
- resetHooks()
-
- logBuf := new(bytes.Buffer)
- ts := httptest.NewUnstartedServer(handler)
-
- tlsConfig := &tls.Config{
- InsecureSkipVerify: true,
- // The h2-14 is temporary, until curl is updated. (as used by unit tests
- // in Docker)
- NextProtos: []string{NextProtoTLS, "h2-14"},
- }
-
- var onlyServer, quiet bool
- for _, opt := range opts {
- switch v := opt.(type) {
- case func(*tls.Config):
- v(tlsConfig)
- case func(*httptest.Server):
- v(ts)
- case serverTesterOpt:
- switch v {
- case optOnlyServer:
- onlyServer = true
- case optQuiet:
- quiet = true
- }
- default:
- t.Fatalf("unknown newServerTester option type %T", v)
- }
- }
-
- ConfigureServer(ts.Config, &Server{})
-
- st := &serverTester{
- t: t,
- ts: ts,
- logBuf: logBuf,
- frc: make(chan Frame, 1),
- frErrc: make(chan error, 1),
- }
- st.hpackEnc = hpack.NewEncoder(&st.headerBuf)
- st.hpackDec = hpack.NewDecoder(initialHeaderTableSize, st.onHeaderField)
-
- ts.TLS = ts.Config.TLSConfig // the httptest.Server has its own copy of this TLS config
- if quiet {
- ts.Config.ErrorLog = log.New(ioutil.Discard, "", 0)
- } else {
- ts.Config.ErrorLog = log.New(io.MultiWriter(stderrv(), twriter{t: t, st: st}, logBuf), "", log.LstdFlags)
- }
- ts.StartTLS()
-
- if VerboseLogs {
- t.Logf("Running test server at: %s", ts.URL)
- }
- testHookGetServerConn = func(v *serverConn) {
- 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 {
- cc, err := tls.Dial("tcp", ts.Listener.Addr().String(), tlsConfig)
- if err != nil {
- t.Fatal(err)
- }
- st.cc = cc
- st.fr = NewFramer(cc, cc)
- }
- return st
-}
-
-func (st *serverTester) closeConn() {
- st.scMu.Lock()
- defer st.scMu.Unlock()
- st.sc.conn.Close()
-}
-
-func (st *serverTester) addLogFilter(phrase string) {
- st.logFilter = append(st.logFilter, phrase)
-}
-
-func (st *serverTester) stream(id uint32) *stream {
- ch := make(chan *stream, 1)
- st.sc.testHookCh <- func(int) {
- ch <- st.sc.streams[id]
- }
- return <-ch
-}
-
-func (st *serverTester) streamState(id uint32) streamState {
- ch := make(chan streamState, 1)
- st.sc.testHookCh <- func(int) {
- state, _ := st.sc.state(id)
- ch <- state
- }
- return <-ch
-}
-
-// 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) {
- lastc <- loopNum
- }
- return <-lastc
-}
-
-// awaitIdle heuristically awaits for the server conn's select loop to be idle.
-// The heuristic is that the server connection's serve loop must schedule
-// 50 times in a row without any channel sends or receives occuring.
-func (st *serverTester) awaitIdle() {
- remain := 50
- last := st.loopNum()
- for remain > 0 {
- n := st.loopNum()
- if n == last+1 {
- remain--
- } else {
- remain = 50
- }
- last = n
- }
-}
-
-func (st *serverTester) Close() {
- st.ts.Close()
- if st.cc != nil {
- st.cc.Close()
- }
- log.SetOutput(os.Stderr)
-}
-
-// greet initiates the client's HTTP/2 connection into a state where
-// frames may be sent.
-func (st *serverTester) greet() {
- st.writePreface()
- st.writeInitialSettings()
- st.wantSettings()
- st.writeSettingsAck()
- st.wantSettingsAck()
-}
-
-func (st *serverTester) writePreface() {
- n, err := st.cc.Write(clientPreface)
- if err != nil {
- st.t.Fatalf("Error writing client preface: %v", err)
- }
- if n != len(clientPreface) {
- st.t.Fatalf("Writing client preface, wrote %d bytes; want %d", n, len(clientPreface))
- }
-}
-
-func (st *serverTester) writeInitialSettings() {
- if err := st.fr.WriteSettings(); err != nil {
- st.t.Fatalf("Error writing initial SETTINGS frame from client to server: %v", err)
- }
-}
-
-func (st *serverTester) writeSettingsAck() {
- if err := st.fr.WriteSettingsAck(); err != nil {
- st.t.Fatalf("Error writing ACK of server's SETTINGS: %v", err)
- }
-}
-
-func (st *serverTester) writeHeaders(p HeadersFrameParam) {
- if err := st.fr.WriteHeaders(p); err != nil {
- st.t.Fatalf("Error writing HEADERS: %v", err)
- }
-}
-
-func (st *serverTester) encodeHeaderField(k, v string) {
- err := st.hpackEnc.WriteField(hpack.HeaderField{Name: k, Value: v})
- if err != nil {
- st.t.Fatalf("HPACK encoding error for %q/%q: %v", k, v, err)
- }
-}
-
-// encodeHeaderRaw is the magic-free version of encodeHeader.
-// It takes 0 or more (k, v) pairs and encodes them.
-func (st *serverTester) encodeHeaderRaw(headers ...string) []byte {
- if len(headers)%2 == 1 {
- panic("odd number of kv args")
- }
- st.headerBuf.Reset()
- for len(headers) > 0 {
- k, v := headers[0], headers[1]
- st.encodeHeaderField(k, v)
- headers = headers[2:]
- }
- return st.headerBuf.Bytes()
-}
-
-// encodeHeader encodes headers and returns their HPACK bytes. headers
-// must contain an even number of key/value pairs. There may be
-// multiple pairs for keys (e.g. "cookie"). The :method, :path, and
-// :scheme headers default to GET, / and https.
-func (st *serverTester) encodeHeader(headers ...string) []byte {
- if len(headers)%2 == 1 {
- panic("odd number of kv args")
- }
-
- st.headerBuf.Reset()
-
- if len(headers) == 0 {
- // Fast path, mostly for benchmarks, so test code doesn't pollute
- // profiles when we're looking to improve server allocations.
- st.encodeHeaderField(":method", "GET")
- st.encodeHeaderField(":path", "/")
- st.encodeHeaderField(":scheme", "https")
- return st.headerBuf.Bytes()
- }
-
- if len(headers) == 2 && headers[0] == ":method" {
- // Another fast path for benchmarks.
- st.encodeHeaderField(":method", headers[1])
- st.encodeHeaderField(":path", "/")
- st.encodeHeaderField(":scheme", "https")
- return st.headerBuf.Bytes()
- }
-
- pseudoCount := map[string]int{}
- keys := []string{":method", ":path", ":scheme"}
- vals := map[string][]string{
- ":method": {"GET"},
- ":path": {"/"},
- ":scheme": {"https"},
- }
- for len(headers) > 0 {
- k, v := headers[0], headers[1]
- headers = headers[2:]
- if _, ok := vals[k]; !ok {
- keys = append(keys, k)
- }
- if strings.HasPrefix(k, ":") {
- pseudoCount[k]++
- if pseudoCount[k] == 1 {
- vals[k] = []string{v}
- } else {
- // Allows testing of invalid headers w/ dup pseudo fields.
- vals[k] = append(vals[k], v)
- }
- } else {
- vals[k] = append(vals[k], v)
- }
- }
- for _, k := range keys {
- for _, v := range vals[k] {
- st.encodeHeaderField(k, v)
- }
- }
- return st.headerBuf.Bytes()
-}
-
-// bodylessReq1 writes a HEADERS frames with StreamID 1 and EndStream and EndHeaders set.
-func (st *serverTester) bodylessReq1(headers ...string) {
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1, // clients send odd numbers
- BlockFragment: st.encodeHeader(headers...),
- EndStream: true,
- EndHeaders: true,
- })
-}
-
-func (st *serverTester) writeData(streamID uint32, endStream bool, data []byte) {
- if err := st.fr.WriteData(streamID, endStream, data); err != nil {
- st.t.Fatalf("Error writing DATA: %v", err)
- }
-}
-
-func (st *serverTester) readFrame() (Frame, error) {
- go func() {
- fr, err := st.fr.ReadFrame()
- if err != nil {
- st.frErrc <- err
- } else {
- st.frc <- fr
- }
- }()
- t := st.readTimer
- if t == nil {
- t = time.NewTimer(2 * time.Second)
- st.readTimer = t
- }
- t.Reset(2 * time.Second)
- defer t.Stop()
- select {
- case f := <-st.frc:
- return f, nil
- case err := <-st.frErrc:
- return nil, err
- case <-t.C:
- return nil, errors.New("timeout waiting for frame")
- }
-}
-
-func (st *serverTester) wantHeaders() *HeadersFrame {
- f, err := st.readFrame()
- if err != nil {
- st.t.Fatalf("Error while expecting a HEADERS frame: %v", err)
- }
- hf, ok := f.(*HeadersFrame)
- if !ok {
- st.t.Fatalf("got a %T; want *HeadersFrame", f)
- }
- return hf
-}
-
-func (st *serverTester) wantContinuation() *ContinuationFrame {
- f, err := st.readFrame()
- if err != nil {
- st.t.Fatalf("Error while expecting a CONTINUATION frame: %v", err)
- }
- cf, ok := f.(*ContinuationFrame)
- if !ok {
- st.t.Fatalf("got a %T; want *ContinuationFrame", f)
- }
- return cf
-}
-
-func (st *serverTester) wantData() *DataFrame {
- f, err := st.readFrame()
- if err != nil {
- st.t.Fatalf("Error while expecting a DATA frame: %v", err)
- }
- df, ok := f.(*DataFrame)
- if !ok {
- st.t.Fatalf("got a %T; want *DataFrame", f)
- }
- return df
-}
-
-func (st *serverTester) wantSettings() *SettingsFrame {
- f, err := st.readFrame()
- if err != nil {
- st.t.Fatalf("Error while expecting a SETTINGS frame: %v", err)
- }
- sf, ok := f.(*SettingsFrame)
- if !ok {
- st.t.Fatalf("got a %T; want *SettingsFrame", f)
- }
- return sf
-}
-
-func (st *serverTester) wantPing() *PingFrame {
- f, err := st.readFrame()
- if err != nil {
- st.t.Fatalf("Error while expecting a PING frame: %v", err)
- }
- pf, ok := f.(*PingFrame)
- if !ok {
- st.t.Fatalf("got a %T; want *PingFrame", f)
- }
- return pf
-}
-
-func (st *serverTester) wantGoAway() *GoAwayFrame {
- f, err := st.readFrame()
- if err != nil {
- st.t.Fatalf("Error while expecting a GOAWAY frame: %v", err)
- }
- gf, ok := f.(*GoAwayFrame)
- if !ok {
- st.t.Fatalf("got a %T; want *GoAwayFrame", f)
- }
- return gf
-}
-
-func (st *serverTester) wantRSTStream(streamID uint32, errCode ErrCode) {
- f, err := st.readFrame()
- if err != nil {
- st.t.Fatalf("Error while expecting an RSTStream frame: %v", err)
- }
- rs, ok := f.(*RSTStreamFrame)
- if !ok {
- st.t.Fatalf("got a %T; want *RSTStreamFrame", f)
- }
- if rs.FrameHeader.StreamID != streamID {
- st.t.Fatalf("RSTStream StreamID = %d; want %d", rs.FrameHeader.StreamID, streamID)
- }
- if rs.ErrCode != errCode {
- st.t.Fatalf("RSTStream ErrCode = %d (%s); want %d (%s)", rs.ErrCode, rs.ErrCode, errCode, errCode)
- }
-}
-
-func (st *serverTester) wantWindowUpdate(streamID, incr uint32) {
- f, err := st.readFrame()
- if err != nil {
- st.t.Fatalf("Error while expecting a WINDOW_UPDATE frame: %v", err)
- }
- wu, ok := f.(*WindowUpdateFrame)
- if !ok {
- st.t.Fatalf("got a %T; want *WindowUpdateFrame", f)
- }
- if wu.FrameHeader.StreamID != streamID {
- st.t.Fatalf("WindowUpdate StreamID = %d; want %d", wu.FrameHeader.StreamID, streamID)
- }
- if wu.Increment != incr {
- st.t.Fatalf("WindowUpdate increment = %d; want %d", wu.Increment, incr)
- }
-}
-
-func (st *serverTester) wantSettingsAck() {
- f, err := st.readFrame()
- if err != nil {
- st.t.Fatal(err)
- }
- sf, ok := f.(*SettingsFrame)
- if !ok {
- st.t.Fatalf("Wanting a settings ACK, received a %T", f)
- }
- if !sf.Header().Flags.Has(FlagSettingsAck) {
- st.t.Fatal("Settings Frame didn't have ACK set")
- }
-
-}
-
-func TestServer(t *testing.T) {
- gotReq := make(chan bool, 1)
- st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Foo", "Bar")
- gotReq <- true
- })
- defer st.Close()
-
- covers("3.5", `
- The server connection preface consists of a potentially empty
- SETTINGS frame ([SETTINGS]) that MUST be the first frame the
- server sends in the HTTP/2 connection.
- `)
-
- st.writePreface()
- st.writeInitialSettings()
- st.wantSettings()
- st.writeSettingsAck()
- st.wantSettingsAck()
-
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1, // clients send odd numbers
- BlockFragment: st.encodeHeader(),
- EndStream: true, // no DATA frames
- EndHeaders: true,
- })
-
- select {
- case <-gotReq:
- case <-time.After(2 * time.Second):
- t.Error("timeout waiting for request")
- }
-}
-
-func TestServer_Request_Get(t *testing.T) {
- testServerRequest(t, func(st *serverTester) {
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1, // clients send odd numbers
- BlockFragment: st.encodeHeader("foo-bar", "some-value"),
- EndStream: true, // no DATA frames
- EndHeaders: true,
- })
- }, func(r *http.Request) {
- if r.Method != "GET" {
- t.Errorf("Method = %q; want GET", r.Method)
- }
- if r.URL.Path != "/" {
- t.Errorf("URL.Path = %q; want /", r.URL.Path)
- }
- if r.ContentLength != 0 {
- t.Errorf("ContentLength = %v; want 0", r.ContentLength)
- }
- if r.Close {
- t.Error("Close = true; want false")
- }
- if !strings.Contains(r.RemoteAddr, ":") {
- t.Errorf("RemoteAddr = %q; want something with a colon", r.RemoteAddr)
- }
- if r.Proto != "HTTP/2.0" || r.ProtoMajor != 2 || r.ProtoMinor != 0 {
- t.Errorf("Proto = %q Major=%v,Minor=%v; want HTTP/2.0", r.Proto, r.ProtoMajor, r.ProtoMinor)
- }
- wantHeader := http.Header{
- "Foo-Bar": []string{"some-value"},
- }
- if !reflect.DeepEqual(r.Header, wantHeader) {
- t.Errorf("Header = %#v; want %#v", r.Header, wantHeader)
- }
- if n, err := r.Body.Read([]byte(" ")); err != io.EOF || n != 0 {
- t.Errorf("Read = %d, %v; want 0, EOF", n, err)
- }
- })
-}
-
-func TestServer_Request_Get_PathSlashes(t *testing.T) {
- testServerRequest(t, func(st *serverTester) {
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1, // clients send odd numbers
- BlockFragment: st.encodeHeader(":path", "/%2f/"),
- EndStream: true, // no DATA frames
- EndHeaders: true,
- })
- }, func(r *http.Request) {
- if r.RequestURI != "/%2f/" {
- t.Errorf("RequestURI = %q; want /%%2f/", r.RequestURI)
- }
- if r.URL.Path != "///" {
- t.Errorf("URL.Path = %q; want ///", r.URL.Path)
- }
- })
-}
-
-// TODO: add a test with EndStream=true on the HEADERS but setting a
-// Content-Length anyway. Should we just omit it and force it to
-// zero?
-
-func TestServer_Request_Post_NoContentLength_EndStream(t *testing.T) {
- testServerRequest(t, func(st *serverTester) {
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1, // clients send odd numbers
- BlockFragment: st.encodeHeader(":method", "POST"),
- EndStream: true,
- EndHeaders: true,
- })
- }, func(r *http.Request) {
- if r.Method != "POST" {
- t.Errorf("Method = %q; want POST", r.Method)
- }
- if r.ContentLength != 0 {
- t.Errorf("ContentLength = %v; want 0", r.ContentLength)
- }
- if n, err := r.Body.Read([]byte(" ")); err != io.EOF || n != 0 {
- t.Errorf("Read = %d, %v; want 0, EOF", n, err)
- }
- })
-}
-
-func TestServer_Request_Post_Body_ImmediateEOF(t *testing.T) {
- testBodyContents(t, -1, "", func(st *serverTester) {
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1, // clients send odd numbers
- BlockFragment: st.encodeHeader(":method", "POST"),
- EndStream: false, // to say DATA frames are coming
- EndHeaders: true,
- })
- st.writeData(1, true, nil) // just kidding. empty body.
- })
-}
-
-func TestServer_Request_Post_Body_OneData(t *testing.T) {
- const content = "Some content"
- testBodyContents(t, -1, content, func(st *serverTester) {
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1, // clients send odd numbers
- BlockFragment: st.encodeHeader(":method", "POST"),
- EndStream: false, // to say DATA frames are coming
- EndHeaders: true,
- })
- st.writeData(1, true, []byte(content))
- })
-}
-
-func TestServer_Request_Post_Body_TwoData(t *testing.T) {
- const content = "Some content"
- testBodyContents(t, -1, content, func(st *serverTester) {
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1, // clients send odd numbers
- BlockFragment: st.encodeHeader(":method", "POST"),
- EndStream: false, // to say DATA frames are coming
- EndHeaders: true,
- })
- st.writeData(1, false, []byte(content[:5]))
- st.writeData(1, true, []byte(content[5:]))
- })
-}
-
-func TestServer_Request_Post_Body_ContentLength_Correct(t *testing.T) {
- const content = "Some content"
- testBodyContents(t, int64(len(content)), content, func(st *serverTester) {
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1, // clients send odd numbers
- BlockFragment: st.encodeHeader(
- ":method", "POST",
- "content-length", strconv.Itoa(len(content)),
- ),
- EndStream: false, // to say DATA frames are coming
- EndHeaders: true,
- })
- st.writeData(1, true, []byte(content))
- })
-}
-
-func TestServer_Request_Post_Body_ContentLength_TooLarge(t *testing.T) {
- testBodyContentsFail(t, 3, "request declared a Content-Length of 3 but only wrote 2 bytes",
- func(st *serverTester) {
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1, // clients send odd numbers
- BlockFragment: st.encodeHeader(
- ":method", "POST",
- "content-length", "3",
- ),
- EndStream: false, // to say DATA frames are coming
- EndHeaders: true,
- })
- st.writeData(1, true, []byte("12"))
- })
-}
-
-func TestServer_Request_Post_Body_ContentLength_TooSmall(t *testing.T) {
- testBodyContentsFail(t, 4, "sender tried to send more than declared Content-Length of 4 bytes",
- func(st *serverTester) {
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1, // clients send odd numbers
- BlockFragment: st.encodeHeader(
- ":method", "POST",
- "content-length", "4",
- ),
- EndStream: false, // to say DATA frames are coming
- EndHeaders: true,
- })
- st.writeData(1, true, []byte("12345"))
- })
-}
-
-func testBodyContents(t *testing.T, wantContentLength int64, wantBody string, write func(st *serverTester)) {
- testServerRequest(t, write, func(r *http.Request) {
- if r.Method != "POST" {
- t.Errorf("Method = %q; want POST", r.Method)
- }
- if r.ContentLength != wantContentLength {
- t.Errorf("ContentLength = %v; want %d", r.ContentLength, wantContentLength)
- }
- all, err := ioutil.ReadAll(r.Body)
- if err != nil {
- t.Fatal(err)
- }
- if string(all) != wantBody {
- t.Errorf("Read = %q; want %q", all, wantBody)
- }
- if err := r.Body.Close(); err != nil {
- t.Fatalf("Close: %v", err)
- }
- })
-}
-
-func testBodyContentsFail(t *testing.T, wantContentLength int64, wantReadError string, write func(st *serverTester)) {
- testServerRequest(t, write, func(r *http.Request) {
- if r.Method != "POST" {
- t.Errorf("Method = %q; want POST", r.Method)
- }
- if r.ContentLength != wantContentLength {
- t.Errorf("ContentLength = %v; want %d", r.ContentLength, wantContentLength)
- }
- all, err := ioutil.ReadAll(r.Body)
- if err == nil {
- t.Fatalf("expected an error (%q) reading from the body. Successfully read %q instead.",
- wantReadError, all)
- }
- if !strings.Contains(err.Error(), wantReadError) {
- t.Fatalf("Body.Read = %v; want substring %q", err, wantReadError)
- }
- if err := r.Body.Close(); err != nil {
- t.Fatalf("Close: %v", err)
- }
- })
-}
-
-// Using a Host header, instead of :authority
-func TestServer_Request_Get_Host(t *testing.T) {
- const host = "example.com"
- testServerRequest(t, func(st *serverTester) {
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1, // clients send odd numbers
- BlockFragment: st.encodeHeader("host", host),
- EndStream: true,
- EndHeaders: true,
- })
- }, func(r *http.Request) {
- if r.Host != host {
- t.Errorf("Host = %q; want %q", r.Host, host)
- }
- })
-}
-
-// Using an :authority pseudo-header, instead of Host
-func TestServer_Request_Get_Authority(t *testing.T) {
- const host = "example.com"
- testServerRequest(t, func(st *serverTester) {
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1, // clients send odd numbers
- BlockFragment: st.encodeHeader(":authority", host),
- EndStream: true,
- EndHeaders: true,
- })
- }, func(r *http.Request) {
- if r.Host != host {
- t.Errorf("Host = %q; want %q", r.Host, host)
- }
- })
-}
-
-func TestServer_Request_WithContinuation(t *testing.T) {
- wantHeader := http.Header{
- "Foo-One": []string{"value-one"},
- "Foo-Two": []string{"value-two"},
- "Foo-Three": []string{"value-three"},
- }
- testServerRequest(t, func(st *serverTester) {
- fullHeaders := st.encodeHeader(
- "foo-one", "value-one",
- "foo-two", "value-two",
- "foo-three", "value-three",
- )
- remain := fullHeaders
- chunks := 0
- for len(remain) > 0 {
- const maxChunkSize = 5
- chunk := remain
- if len(chunk) > maxChunkSize {
- chunk = chunk[:maxChunkSize]
- }
- remain = remain[len(chunk):]
-
- if chunks == 0 {
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1, // clients send odd numbers
- BlockFragment: chunk,
- EndStream: true, // no DATA frames
- EndHeaders: false, // we'll have continuation frames
- })
- } else {
- err := st.fr.WriteContinuation(1, len(remain) == 0, chunk)
- if err != nil {
- t.Fatal(err)
- }
- }
- chunks++
- }
- if chunks < 2 {
- t.Fatal("too few chunks")
- }
- }, func(r *http.Request) {
- if !reflect.DeepEqual(r.Header, wantHeader) {
- t.Errorf("Header = %#v; want %#v", r.Header, wantHeader)
- }
- })
-}
-
-// Concatenated cookie headers. ("8.1.2.5 Compressing the Cookie Header Field")
-func TestServer_Request_CookieConcat(t *testing.T) {
- const host = "example.com"
- testServerRequest(t, func(st *serverTester) {
- st.bodylessReq1(
- ":authority", host,
- "cookie", "a=b",
- "cookie", "c=d",
- "cookie", "e=f",
- )
- }, func(r *http.Request) {
- const want = "a=b; c=d; e=f"
- if got := r.Header.Get("Cookie"); got != want {
- t.Errorf("Cookie = %q; want %q", got, want)
- }
- })
-}
-
-func TestServer_Request_Reject_CapitalHeader(t *testing.T) {
- testRejectRequest(t, func(st *serverTester) { st.bodylessReq1("UPPER", "v") })
-}
-
-func TestServer_Request_Reject_HeaderFieldNameColon(t *testing.T) {
- testRejectRequest(t, func(st *serverTester) { st.bodylessReq1("has:colon", "v") })
-}
-
-func TestServer_Request_Reject_HeaderFieldNameNULL(t *testing.T) {
- testRejectRequest(t, func(st *serverTester) { st.bodylessReq1("has\x00null", "v") })
-}
-
-func TestServer_Request_Reject_HeaderFieldNameEmpty(t *testing.T) {
- testRejectRequest(t, func(st *serverTester) { st.bodylessReq1("", "v") })
-}
-
-func TestServer_Request_Reject_HeaderFieldValueNewline(t *testing.T) {
- testRejectRequest(t, func(st *serverTester) { st.bodylessReq1("foo", "has\nnewline") })
-}
-
-func TestServer_Request_Reject_HeaderFieldValueCR(t *testing.T) {
- testRejectRequest(t, func(st *serverTester) { st.bodylessReq1("foo", "has\rcarriage") })
-}
-
-func TestServer_Request_Reject_HeaderFieldValueDEL(t *testing.T) {
- testRejectRequest(t, func(st *serverTester) { st.bodylessReq1("foo", "has\x7fdel") })
-}
-
-func TestServer_Request_Reject_Pseudo_Missing_method(t *testing.T) {
- testRejectRequest(t, func(st *serverTester) { st.bodylessReq1(":method", "") })
-}
-
-func TestServer_Request_Reject_Pseudo_ExactlyOne(t *testing.T) {
- // 8.1.2.3 Request Pseudo-Header Fields
- // "All HTTP/2 requests MUST include exactly one valid value" ...
- testRejectRequest(t, func(st *serverTester) {
- st.addLogFilter("duplicate pseudo-header")
- st.bodylessReq1(":method", "GET", ":method", "POST")
- })
-}
-
-func TestServer_Request_Reject_Pseudo_AfterRegular(t *testing.T) {
- // 8.1.2.3 Request Pseudo-Header Fields
- // "All pseudo-header fields MUST appear in the header block
- // before regular header fields. Any request or response that
- // contains a pseudo-header field that appears in a header
- // block after a regular header field MUST be treated as
- // malformed (Section 8.1.2.6)."
- testRejectRequest(t, func(st *serverTester) {
- st.addLogFilter("pseudo-header after regular header")
- var buf bytes.Buffer
- enc := hpack.NewEncoder(&buf)
- enc.WriteField(hpack.HeaderField{Name: ":method", Value: "GET"})
- enc.WriteField(hpack.HeaderField{Name: "regular", Value: "foobar"})
- enc.WriteField(hpack.HeaderField{Name: ":path", Value: "/"})
- enc.WriteField(hpack.HeaderField{Name: ":scheme", Value: "https"})
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1, // clients send odd numbers
- BlockFragment: buf.Bytes(),
- EndStream: true,
- EndHeaders: true,
- })
- })
-}
-
-func TestServer_Request_Reject_Pseudo_Missing_path(t *testing.T) {
- testRejectRequest(t, func(st *serverTester) { st.bodylessReq1(":path", "") })
-}
-
-func TestServer_Request_Reject_Pseudo_Missing_scheme(t *testing.T) {
- testRejectRequest(t, func(st *serverTester) { st.bodylessReq1(":scheme", "") })
-}
-
-func TestServer_Request_Reject_Pseudo_scheme_invalid(t *testing.T) {
- testRejectRequest(t, func(st *serverTester) { st.bodylessReq1(":scheme", "bogus") })
-}
-
-func TestServer_Request_Reject_Pseudo_Unknown(t *testing.T) {
- testRejectRequest(t, func(st *serverTester) {
- st.addLogFilter(`invalid pseudo-header ":unknown_thing"`)
- st.bodylessReq1(":unknown_thing", "")
- })
-}
-
-func testRejectRequest(t *testing.T, send func(*serverTester)) {
- st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
- t.Fatal("server request made it to handler; should've been rejected")
- })
- defer st.Close()
-
- st.greet()
- send(st)
- st.wantRSTStream(1, ErrCodeProtocol)
-}
-
-func TestServer_Request_Connect(t *testing.T) {
- testServerRequest(t, func(st *serverTester) {
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1,
- BlockFragment: st.encodeHeaderRaw(
- ":method", "CONNECT",
- ":authority", "example.com:123",
- ),
- EndStream: true,
- EndHeaders: true,
- })
- }, func(r *http.Request) {
- if g, w := r.Method, "CONNECT"; g != w {
- t.Errorf("Method = %q; want %q", g, w)
- }
- if g, w := r.RequestURI, "example.com:123"; g != w {
- t.Errorf("RequestURI = %q; want %q", g, w)
- }
- if g, w := r.URL.Host, "example.com:123"; g != w {
- t.Errorf("URL.Host = %q; want %q", g, w)
- }
- })
-}
-
-func TestServer_Request_Connect_InvalidPath(t *testing.T) {
- testServerRejectsStream(t, ErrCodeProtocol, func(st *serverTester) {
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1,
- BlockFragment: st.encodeHeaderRaw(
- ":method", "CONNECT",
- ":authority", "example.com:123",
- ":path", "/bogus",
- ),
- EndStream: true,
- EndHeaders: true,
- })
- })
-}
-
-func TestServer_Request_Connect_InvalidScheme(t *testing.T) {
- testServerRejectsStream(t, ErrCodeProtocol, func(st *serverTester) {
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1,
- BlockFragment: st.encodeHeaderRaw(
- ":method", "CONNECT",
- ":authority", "example.com:123",
- ":scheme", "https",
- ),
- EndStream: true,
- EndHeaders: true,
- })
- })
-}
-
-func TestServer_Ping(t *testing.T) {
- st := newServerTester(t, nil)
- defer st.Close()
- st.greet()
-
- // Server should ignore this one, since it has ACK set.
- ackPingData := [8]byte{1, 2, 4, 8, 16, 32, 64, 128}
- if err := st.fr.WritePing(true, ackPingData); err != nil {
- t.Fatal(err)
- }
-
- // But the server should reply to this one, since ACK is false.
- pingData := [8]byte{1, 2, 3, 4, 5, 6, 7, 8}
- if err := st.fr.WritePing(false, pingData); err != nil {
- t.Fatal(err)
- }
-
- pf := st.wantPing()
- if !pf.Flags.Has(FlagPingAck) {
- t.Error("response ping doesn't have ACK set")
- }
- if pf.Data != pingData {
- t.Errorf("response ping has data %q; want %q", pf.Data, pingData)
- }
-}
-
-func TestServer_RejectsLargeFrames(t *testing.T) {
- if runtime.GOOS == "windows" {
- t.Skip("see golang.org/issue/13434")
- }
-
- st := newServerTester(t, nil)
- defer st.Close()
- st.greet()
-
- // Write too large of a frame (too large by one byte)
- // We ignore the return value because it's expected that the server
- // will only read the first 9 bytes (the headre) and then disconnect.
- st.fr.WriteRawFrame(0xff, 0, 0, make([]byte, defaultMaxReadFrameSize+1))
-
- gf := st.wantGoAway()
- if gf.ErrCode != ErrCodeFrameSize {
- t.Errorf("GOAWAY err = %v; want %v", gf.ErrCode, ErrCodeFrameSize)
- }
- if st.logBuf.Len() != 0 {
- // Previously we spun here for a bit until the GOAWAY disconnect
- // timer fired, logging while we fired.
- t.Errorf("unexpected server output: %.500s\n", st.logBuf.Bytes())
- }
-}
-
-func TestServer_Handler_Sends_WindowUpdate(t *testing.T) {
- puppet := newHandlerPuppet()
- st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
- puppet.act(w, r)
- })
- defer st.Close()
- defer puppet.done()
-
- st.greet()
-
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1, // clients send odd numbers
- BlockFragment: st.encodeHeader(":method", "POST"),
- EndStream: false, // data coming
- EndHeaders: true,
- })
- st.writeData(1, false, []byte("abcdef"))
- puppet.do(readBodyHandler(t, "abc"))
- st.wantWindowUpdate(0, 3)
- st.wantWindowUpdate(1, 3)
-
- puppet.do(readBodyHandler(t, "def"))
- st.wantWindowUpdate(0, 3)
- st.wantWindowUpdate(1, 3)
-
- st.writeData(1, true, []byte("ghijkl")) // END_STREAM here
- puppet.do(readBodyHandler(t, "ghi"))
- puppet.do(readBodyHandler(t, "jkl"))
- st.wantWindowUpdate(0, 3)
- st.wantWindowUpdate(0, 3) // no more stream-level, since END_STREAM
-}
-
-func TestServer_Send_GoAway_After_Bogus_WindowUpdate(t *testing.T) {
- st := newServerTester(t, nil)
- defer st.Close()
- st.greet()
- if err := st.fr.WriteWindowUpdate(0, 1<<31-1); err != nil {
- t.Fatal(err)
- }
- gf := st.wantGoAway()
- if gf.ErrCode != ErrCodeFlowControl {
- t.Errorf("GOAWAY err = %v; want %v", gf.ErrCode, ErrCodeFlowControl)
- }
- if gf.LastStreamID != 0 {
- t.Errorf("GOAWAY last stream ID = %v; want %v", gf.LastStreamID, 0)
- }
-}
-
-func TestServer_Send_RstStream_After_Bogus_WindowUpdate(t *testing.T) {
- inHandler := make(chan bool)
- blockHandler := make(chan bool)
- st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
- inHandler <- true
- <-blockHandler
- })
- defer st.Close()
- defer close(blockHandler)
- st.greet()
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1,
- BlockFragment: st.encodeHeader(":method", "POST"),
- EndStream: false, // keep it open
- EndHeaders: true,
- })
- <-inHandler
- // Send a bogus window update:
- if err := st.fr.WriteWindowUpdate(1, 1<<31-1); err != nil {
- t.Fatal(err)
- }
- st.wantRSTStream(1, ErrCodeFlowControl)
-}
-
-// testServerPostUnblock sends a hanging POST with unsent data to handler,
-// then runs fn once in the handler, and verifies that the error returned from
-// handler is acceptable. It fails if takes over 5 seconds for handler to exit.
-func testServerPostUnblock(t *testing.T,
- handler func(http.ResponseWriter, *http.Request) error,
- fn func(*serverTester),
- checkErr func(error),
- otherHeaders ...string) {
- inHandler := make(chan bool)
- errc := make(chan error, 1)
- st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
- inHandler <- true
- errc <- handler(w, r)
- })
- st.greet()
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1,
- BlockFragment: st.encodeHeader(append([]string{":method", "POST"}, otherHeaders...)...),
- EndStream: false, // keep it open
- EndHeaders: true,
- })
- <-inHandler
- fn(st)
- select {
- case err := <-errc:
- if checkErr != nil {
- checkErr(err)
- }
- case <-time.After(5 * time.Second):
- t.Fatal("timeout waiting for Handler to return")
- }
- st.Close()
-}
-
-func TestServer_RSTStream_Unblocks_Read(t *testing.T) {
- testServerPostUnblock(t,
- func(w http.ResponseWriter, r *http.Request) (err error) {
- _, err = r.Body.Read(make([]byte, 1))
- return
- },
- func(st *serverTester) {
- if err := st.fr.WriteRSTStream(1, ErrCodeCancel); err != nil {
- t.Fatal(err)
- }
- },
- func(err error) {
- want := StreamError{StreamID: 0x1, Code: 0x8}
- if !reflect.DeepEqual(err, want) {
- t.Errorf("Read error = %v; want %v", err, want)
- }
- },
- )
-}
-
-func TestServer_RSTStream_Unblocks_Header_Write(t *testing.T) {
- // Run this test a bunch, because it doesn't always
- // deadlock. But with a bunch, it did.
- n := 50
- if testing.Short() {
- n = 5
- }
- for i := 0; i < n; i++ {
- testServer_RSTStream_Unblocks_Header_Write(t)
- }
-}
-
-func testServer_RSTStream_Unblocks_Header_Write(t *testing.T) {
- inHandler := make(chan bool, 1)
- unblockHandler := make(chan bool, 1)
- headerWritten := make(chan bool, 1)
- wroteRST := make(chan bool, 1)
-
- st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
- inHandler <- true
- <-wroteRST
- w.Header().Set("foo", "bar")
- w.WriteHeader(200)
- w.(http.Flusher).Flush()
- headerWritten <- true
- <-unblockHandler
- })
- defer st.Close()
-
- st.greet()
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1,
- BlockFragment: st.encodeHeader(":method", "POST"),
- EndStream: false, // keep it open
- EndHeaders: true,
- })
- <-inHandler
- if err := st.fr.WriteRSTStream(1, ErrCodeCancel); err != nil {
- t.Fatal(err)
- }
- wroteRST <- true
- st.awaitIdle()
- select {
- case <-headerWritten:
- case <-time.After(2 * time.Second):
- t.Error("timeout waiting for header write")
- }
- unblockHandler <- true
-}
-
-func TestServer_DeadConn_Unblocks_Read(t *testing.T) {
- testServerPostUnblock(t,
- func(w http.ResponseWriter, r *http.Request) (err error) {
- _, err = r.Body.Read(make([]byte, 1))
- return
- },
- func(st *serverTester) { st.cc.Close() },
- func(err error) {
- if err == nil {
- t.Error("unexpected nil error from Request.Body.Read")
- }
- },
- )
-}
-
-var blockUntilClosed = func(w http.ResponseWriter, r *http.Request) error {
- <-w.(http.CloseNotifier).CloseNotify()
- return nil
-}
-
-func TestServer_CloseNotify_After_RSTStream(t *testing.T) {
- testServerPostUnblock(t, blockUntilClosed, func(st *serverTester) {
- if err := st.fr.WriteRSTStream(1, ErrCodeCancel); err != nil {
- t.Fatal(err)
- }
- }, nil)
-}
-
-func TestServer_CloseNotify_After_ConnClose(t *testing.T) {
- testServerPostUnblock(t, blockUntilClosed, func(st *serverTester) { st.cc.Close() }, nil)
-}
-
-// that CloseNotify unblocks after a stream error due to the client's
-// problem that's unrelated to them explicitly canceling it (which is
-// TestServer_CloseNotify_After_RSTStream above)
-func TestServer_CloseNotify_After_StreamError(t *testing.T) {
- testServerPostUnblock(t, blockUntilClosed, func(st *serverTester) {
- // data longer than declared Content-Length => stream error
- st.writeData(1, true, []byte("1234"))
- }, nil, "content-length", "3")
-}
-
-func TestServer_StateTransitions(t *testing.T) {
- var st *serverTester
- inHandler := make(chan bool)
- writeData := make(chan bool)
- leaveHandler := make(chan bool)
- st = newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
- inHandler <- true
- if st.stream(1) == nil {
- t.Errorf("nil stream 1 in handler")
- }
- if got, want := st.streamState(1), stateOpen; got != want {
- t.Errorf("in handler, state is %v; want %v", got, want)
- }
- writeData <- true
- if n, err := r.Body.Read(make([]byte, 1)); n != 0 || err != io.EOF {
- t.Errorf("body read = %d, %v; want 0, EOF", n, err)
- }
- if got, want := st.streamState(1), stateHalfClosedRemote; got != want {
- t.Errorf("in handler, state is %v; want %v", got, want)
- }
-
- <-leaveHandler
- })
- st.greet()
- if st.stream(1) != nil {
- t.Fatal("stream 1 should be empty")
- }
- if got := st.streamState(1); got != stateIdle {
- t.Fatalf("stream 1 should be idle; got %v", got)
- }
-
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1,
- BlockFragment: st.encodeHeader(":method", "POST"),
- EndStream: false, // keep it open
- EndHeaders: true,
- })
- <-inHandler
- <-writeData
- st.writeData(1, true, nil)
-
- leaveHandler <- true
- hf := st.wantHeaders()
- if !hf.StreamEnded() {
- t.Fatal("expected END_STREAM flag")
- }
-
- if got, want := st.streamState(1), stateClosed; got != want {
- t.Errorf("at end, state is %v; want %v", got, want)
- }
- if st.stream(1) != nil {
- t.Fatal("at end, stream 1 should be gone")
- }
-}
-
-// test HEADERS w/o EndHeaders + another HEADERS (should get rejected)
-func TestServer_Rejects_HeadersNoEnd_Then_Headers(t *testing.T) {
- testServerRejectsConn(t, func(st *serverTester) {
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1,
- BlockFragment: st.encodeHeader(),
- EndStream: true,
- EndHeaders: false,
- })
- st.writeHeaders(HeadersFrameParam{ // Not a continuation.
- StreamID: 3, // different stream.
- BlockFragment: st.encodeHeader(),
- EndStream: true,
- EndHeaders: true,
- })
- })
-}
-
-// test HEADERS w/o EndHeaders + PING (should get rejected)
-func TestServer_Rejects_HeadersNoEnd_Then_Ping(t *testing.T) {
- testServerRejectsConn(t, func(st *serverTester) {
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1,
- BlockFragment: st.encodeHeader(),
- EndStream: true,
- EndHeaders: false,
- })
- if err := st.fr.WritePing(false, [8]byte{}); err != nil {
- t.Fatal(err)
- }
- })
-}
-
-// test HEADERS w/ EndHeaders + a continuation HEADERS (should get rejected)
-func TestServer_Rejects_HeadersEnd_Then_Continuation(t *testing.T) {
- testServerRejectsConn(t, func(st *serverTester) {
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1,
- BlockFragment: st.encodeHeader(),
- EndStream: true,
- EndHeaders: true,
- })
- st.wantHeaders()
- if err := st.fr.WriteContinuation(1, true, encodeHeaderNoImplicit(t, "foo", "bar")); err != nil {
- t.Fatal(err)
- }
- })
-}
-
-// test HEADERS w/o EndHeaders + a continuation HEADERS on wrong stream ID
-func TestServer_Rejects_HeadersNoEnd_Then_ContinuationWrongStream(t *testing.T) {
- testServerRejectsConn(t, func(st *serverTester) {
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1,
- BlockFragment: st.encodeHeader(),
- EndStream: true,
- EndHeaders: false,
- })
- if err := st.fr.WriteContinuation(3, true, encodeHeaderNoImplicit(t, "foo", "bar")); err != nil {
- t.Fatal(err)
- }
- })
-}
-
-// No HEADERS on stream 0.
-func TestServer_Rejects_Headers0(t *testing.T) {
- testServerRejectsConn(t, func(st *serverTester) {
- st.fr.AllowIllegalWrites = true
- st.writeHeaders(HeadersFrameParam{
- StreamID: 0,
- BlockFragment: st.encodeHeader(),
- EndStream: true,
- EndHeaders: true,
- })
- })
-}
-
-// No CONTINUATION on stream 0.
-func TestServer_Rejects_Continuation0(t *testing.T) {
- testServerRejectsConn(t, func(st *serverTester) {
- st.fr.AllowIllegalWrites = true
- if err := st.fr.WriteContinuation(0, true, st.encodeHeader()); err != nil {
- t.Fatal(err)
- }
- })
-}
-
-func TestServer_Rejects_PushPromise(t *testing.T) {
- testServerRejectsConn(t, func(st *serverTester) {
- pp := PushPromiseParam{
- StreamID: 1,
- PromiseID: 3,
- }
- if err := st.fr.WritePushPromise(pp); err != nil {
- t.Fatal(err)
- }
- })
-}
-
-// testServerRejectsConn tests that the server hangs up with a GOAWAY
-// frame and a server close after the client does something
-// deserving a CONNECTION_ERROR.
-func testServerRejectsConn(t *testing.T, writeReq func(*serverTester)) {
- st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {})
- st.addLogFilter("connection error: PROTOCOL_ERROR")
- defer st.Close()
- st.greet()
- writeReq(st)
-
- st.wantGoAway()
- errc := make(chan error, 1)
- go func() {
- fr, err := st.fr.ReadFrame()
- if err == nil {
- err = fmt.Errorf("got frame of type %T", fr)
- }
- errc <- err
- }()
- select {
- case err := <-errc:
- if err != io.EOF {
- t.Errorf("ReadFrame = %v; want io.EOF", err)
- }
- case <-time.After(2 * time.Second):
- t.Error("timeout waiting for disconnect")
- }
-}
-
-// testServerRejectsStream tests that the server sends a RST_STREAM with the provided
-// error code after a client sends a bogus request.
-func testServerRejectsStream(t *testing.T, code ErrCode, writeReq func(*serverTester)) {
- st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {})
- defer st.Close()
- st.greet()
- writeReq(st)
- st.wantRSTStream(1, code)
-}
-
-// testServerRequest sets up an idle HTTP/2 connection and lets you
-// write a single request with writeReq, and then verify that the
-// *http.Request is built correctly in checkReq.
-func testServerRequest(t *testing.T, writeReq func(*serverTester), checkReq func(*http.Request)) {
- gotReq := make(chan bool, 1)
- st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
- if r.Body == nil {
- t.Fatal("nil Body")
- }
- checkReq(r)
- gotReq <- true
- })
- defer st.Close()
-
- st.greet()
- writeReq(st)
-
- select {
- case <-gotReq:
- case <-time.After(2 * time.Second):
- t.Error("timeout waiting for request")
- }
-}
-
-func getSlash(st *serverTester) { st.bodylessReq1() }
-
-func TestServer_Response_NoData(t *testing.T) {
- testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
- // Nothing.
- return nil
- }, func(st *serverTester) {
- getSlash(st)
- hf := st.wantHeaders()
- if !hf.StreamEnded() {
- t.Fatal("want END_STREAM flag")
- }
- if !hf.HeadersEnded() {
- t.Fatal("want END_HEADERS flag")
- }
- })
-}
-
-func TestServer_Response_NoData_Header_FooBar(t *testing.T) {
- testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
- w.Header().Set("Foo-Bar", "some-value")
- return nil
- }, func(st *serverTester) {
- getSlash(st)
- hf := st.wantHeaders()
- if !hf.StreamEnded() {
- t.Fatal("want END_STREAM flag")
- }
- if !hf.HeadersEnded() {
- t.Fatal("want END_HEADERS flag")
- }
- goth := st.decodeHeader(hf.HeaderBlockFragment())
- wanth := [][2]string{
- {":status", "200"},
- {"foo-bar", "some-value"},
- {"content-type", "text/plain; charset=utf-8"},
- {"content-length", "0"},
- }
- if !reflect.DeepEqual(goth, wanth) {
- t.Errorf("Got headers %v; want %v", goth, wanth)
- }
- })
-}
-
-func TestServer_Response_Data_Sniff_DoesntOverride(t *testing.T) {
- const msg = "this is HTML."
- testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
- w.Header().Set("Content-Type", "foo/bar")
- io.WriteString(w, msg)
- return nil
- }, func(st *serverTester) {
- getSlash(st)
- hf := st.wantHeaders()
- if hf.StreamEnded() {
- t.Fatal("don't want END_STREAM, expecting data")
- }
- if !hf.HeadersEnded() {
- t.Fatal("want END_HEADERS flag")
- }
- goth := st.decodeHeader(hf.HeaderBlockFragment())
- wanth := [][2]string{
- {":status", "200"},
- {"content-type", "foo/bar"},
- {"content-length", strconv.Itoa(len(msg))},
- }
- if !reflect.DeepEqual(goth, wanth) {
- t.Errorf("Got headers %v; want %v", goth, wanth)
- }
- df := st.wantData()
- if !df.StreamEnded() {
- t.Error("expected DATA to have END_STREAM flag")
- }
- if got := string(df.Data()); got != msg {
- t.Errorf("got DATA %q; want %q", got, msg)
- }
- })
-}
-
-func TestServer_Response_TransferEncoding_chunked(t *testing.T) {
- const msg = "hi"
- testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
- w.Header().Set("Transfer-Encoding", "chunked") // should be stripped
- io.WriteString(w, msg)
- return nil
- }, func(st *serverTester) {
- getSlash(st)
- hf := st.wantHeaders()
- goth := st.decodeHeader(hf.HeaderBlockFragment())
- wanth := [][2]string{
- {":status", "200"},
- {"content-type", "text/plain; charset=utf-8"},
- {"content-length", strconv.Itoa(len(msg))},
- }
- if !reflect.DeepEqual(goth, wanth) {
- t.Errorf("Got headers %v; want %v", goth, wanth)
- }
- })
-}
-
-// Header accessed only after the initial write.
-func TestServer_Response_Data_IgnoreHeaderAfterWrite_After(t *testing.T) {
- const msg = "this is HTML."
- testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
- io.WriteString(w, msg)
- w.Header().Set("foo", "should be ignored")
- return nil
- }, func(st *serverTester) {
- getSlash(st)
- hf := st.wantHeaders()
- if hf.StreamEnded() {
- t.Fatal("unexpected END_STREAM")
- }
- if !hf.HeadersEnded() {
- t.Fatal("want END_HEADERS flag")
- }
- goth := st.decodeHeader(hf.HeaderBlockFragment())
- wanth := [][2]string{
- {":status", "200"},
- {"content-type", "text/html; charset=utf-8"},
- {"content-length", strconv.Itoa(len(msg))},
- }
- if !reflect.DeepEqual(goth, wanth) {
- t.Errorf("Got headers %v; want %v", goth, wanth)
- }
- })
-}
-
-// Header accessed before the initial write and later mutated.
-func TestServer_Response_Data_IgnoreHeaderAfterWrite_Overwrite(t *testing.T) {
- const msg = "this is HTML."
- testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
- w.Header().Set("foo", "proper value")
- io.WriteString(w, msg)
- w.Header().Set("foo", "should be ignored")
- return nil
- }, func(st *serverTester) {
- getSlash(st)
- hf := st.wantHeaders()
- if hf.StreamEnded() {
- t.Fatal("unexpected END_STREAM")
- }
- if !hf.HeadersEnded() {
- t.Fatal("want END_HEADERS flag")
- }
- goth := st.decodeHeader(hf.HeaderBlockFragment())
- wanth := [][2]string{
- {":status", "200"},
- {"foo", "proper value"},
- {"content-type", "text/html; charset=utf-8"},
- {"content-length", strconv.Itoa(len(msg))},
- }
- if !reflect.DeepEqual(goth, wanth) {
- t.Errorf("Got headers %v; want %v", goth, wanth)
- }
- })
-}
-
-func TestServer_Response_Data_SniffLenType(t *testing.T) {
- const msg = "this is HTML."
- testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
- io.WriteString(w, msg)
- return nil
- }, func(st *serverTester) {
- getSlash(st)
- hf := st.wantHeaders()
- if hf.StreamEnded() {
- t.Fatal("don't want END_STREAM, expecting data")
- }
- if !hf.HeadersEnded() {
- t.Fatal("want END_HEADERS flag")
- }
- goth := st.decodeHeader(hf.HeaderBlockFragment())
- wanth := [][2]string{
- {":status", "200"},
- {"content-type", "text/html; charset=utf-8"},
- {"content-length", strconv.Itoa(len(msg))},
- }
- if !reflect.DeepEqual(goth, wanth) {
- t.Errorf("Got headers %v; want %v", goth, wanth)
- }
- df := st.wantData()
- if !df.StreamEnded() {
- t.Error("expected DATA to have END_STREAM flag")
- }
- if got := string(df.Data()); got != msg {
- t.Errorf("got DATA %q; want %q", got, msg)
- }
- })
-}
-
-func TestServer_Response_Header_Flush_MidWrite(t *testing.T) {
- const msg = "this is HTML"
- const msg2 = ", and this is the next chunk"
- testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
- io.WriteString(w, msg)
- w.(http.Flusher).Flush()
- io.WriteString(w, msg2)
- return nil
- }, func(st *serverTester) {
- getSlash(st)
- hf := st.wantHeaders()
- if hf.StreamEnded() {
- t.Fatal("unexpected END_STREAM flag")
- }
- if !hf.HeadersEnded() {
- t.Fatal("want END_HEADERS flag")
- }
- goth := st.decodeHeader(hf.HeaderBlockFragment())
- wanth := [][2]string{
- {":status", "200"},
- {"content-type", "text/html; charset=utf-8"}, // sniffed
- // and no content-length
- }
- if !reflect.DeepEqual(goth, wanth) {
- t.Errorf("Got headers %v; want %v", goth, wanth)
- }
- {
- df := st.wantData()
- if df.StreamEnded() {
- t.Error("unexpected END_STREAM flag")
- }
- if got := string(df.Data()); got != msg {
- t.Errorf("got DATA %q; want %q", got, msg)
- }
- }
- {
- df := st.wantData()
- if !df.StreamEnded() {
- t.Error("wanted END_STREAM flag on last data chunk")
- }
- if got := string(df.Data()); got != msg2 {
- t.Errorf("got DATA %q; want %q", got, msg2)
- }
- }
- })
-}
-
-func TestServer_Response_LargeWrite(t *testing.T) {
- const size = 1 << 20
- const maxFrameSize = 16 << 10
- testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
- n, err := w.Write(bytes.Repeat([]byte("a"), size))
- if err != nil {
- return fmt.Errorf("Write error: %v", err)
- }
- if n != size {
- return fmt.Errorf("wrong size %d from Write", n)
- }
- return nil
- }, func(st *serverTester) {
- if err := st.fr.WriteSettings(
- Setting{SettingInitialWindowSize, 0},
- Setting{SettingMaxFrameSize, maxFrameSize},
- ); err != nil {
- t.Fatal(err)
- }
- st.wantSettingsAck()
-
- getSlash(st) // make the single request
-
- // Give the handler quota to write:
- if err := st.fr.WriteWindowUpdate(1, size); err != nil {
- t.Fatal(err)
- }
- // Give the handler quota to write to connection-level
- // window as well
- if err := st.fr.WriteWindowUpdate(0, size); err != nil {
- t.Fatal(err)
- }
- hf := st.wantHeaders()
- if hf.StreamEnded() {
- t.Fatal("unexpected END_STREAM flag")
- }
- if !hf.HeadersEnded() {
- t.Fatal("want END_HEADERS flag")
- }
- goth := st.decodeHeader(hf.HeaderBlockFragment())
- wanth := [][2]string{
- {":status", "200"},
- {"content-type", "text/plain; charset=utf-8"}, // sniffed
- // and no content-length
- }
- if !reflect.DeepEqual(goth, wanth) {
- t.Errorf("Got headers %v; want %v", goth, wanth)
- }
- var bytes, frames int
- for {
- df := st.wantData()
- bytes += len(df.Data())
- frames++
- for _, b := range df.Data() {
- if b != 'a' {
- t.Fatal("non-'a' byte seen in DATA")
- }
- }
- if df.StreamEnded() {
- break
- }
- }
- if bytes != size {
- t.Errorf("Got %d bytes; want %d", bytes, size)
- }
- if want := int(size / maxFrameSize); frames < want || frames > want*2 {
- t.Errorf("Got %d frames; want %d", frames, size)
- }
- })
-}
-
-// Test that the handler can't write more than the client allows
-func TestServer_Response_LargeWrite_FlowControlled(t *testing.T) {
- const size = 1 << 20
- const maxFrameSize = 16 << 10
- testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
- w.(http.Flusher).Flush()
- n, err := w.Write(bytes.Repeat([]byte("a"), size))
- if err != nil {
- return fmt.Errorf("Write error: %v", err)
- }
- if n != size {
- return fmt.Errorf("wrong size %d from Write", n)
- }
- return nil
- }, func(st *serverTester) {
- // Set the window size to something explicit for this test.
- // It's also how much initial data we expect.
- const initWindowSize = 123
- if err := st.fr.WriteSettings(
- Setting{SettingInitialWindowSize, initWindowSize},
- Setting{SettingMaxFrameSize, maxFrameSize},
- ); err != nil {
- t.Fatal(err)
- }
- st.wantSettingsAck()
-
- getSlash(st) // make the single request
- defer func() { st.fr.WriteRSTStream(1, ErrCodeCancel) }()
-
- hf := st.wantHeaders()
- if hf.StreamEnded() {
- t.Fatal("unexpected END_STREAM flag")
- }
- if !hf.HeadersEnded() {
- t.Fatal("want END_HEADERS flag")
- }
-
- df := st.wantData()
- if got := len(df.Data()); got != initWindowSize {
- t.Fatalf("Initial window size = %d but got DATA with %d bytes", initWindowSize, got)
- }
-
- for _, quota := range []int{1, 13, 127} {
- if err := st.fr.WriteWindowUpdate(1, uint32(quota)); err != nil {
- t.Fatal(err)
- }
- df := st.wantData()
- if int(quota) != len(df.Data()) {
- t.Fatalf("read %d bytes after giving %d quota", len(df.Data()), quota)
- }
- }
-
- if err := st.fr.WriteRSTStream(1, ErrCodeCancel); err != nil {
- t.Fatal(err)
- }
- })
-}
-
-// Test that the handler blocked in a Write is unblocked if the server sends a RST_STREAM.
-func TestServer_Response_RST_Unblocks_LargeWrite(t *testing.T) {
- const size = 1 << 20
- const maxFrameSize = 16 << 10
- testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
- w.(http.Flusher).Flush()
- errc := make(chan error, 1)
- go func() {
- _, err := w.Write(bytes.Repeat([]byte("a"), size))
- errc <- err
- }()
- select {
- case err := <-errc:
- if err == nil {
- return errors.New("unexpected nil error from Write in handler")
- }
- return nil
- case <-time.After(2 * time.Second):
- return errors.New("timeout waiting for Write in handler")
- }
- }, func(st *serverTester) {
- if err := st.fr.WriteSettings(
- Setting{SettingInitialWindowSize, 0},
- Setting{SettingMaxFrameSize, maxFrameSize},
- ); err != nil {
- t.Fatal(err)
- }
- st.wantSettingsAck()
-
- getSlash(st) // make the single request
-
- hf := st.wantHeaders()
- if hf.StreamEnded() {
- t.Fatal("unexpected END_STREAM flag")
- }
- if !hf.HeadersEnded() {
- t.Fatal("want END_HEADERS flag")
- }
-
- if err := st.fr.WriteRSTStream(1, ErrCodeCancel); err != nil {
- t.Fatal(err)
- }
- })
-}
-
-func TestServer_Response_Empty_Data_Not_FlowControlled(t *testing.T) {
- testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
- w.(http.Flusher).Flush()
- // Nothing; send empty DATA
- return nil
- }, func(st *serverTester) {
- // Handler gets no data quota:
- if err := st.fr.WriteSettings(Setting{SettingInitialWindowSize, 0}); err != nil {
- t.Fatal(err)
- }
- st.wantSettingsAck()
-
- getSlash(st) // make the single request
-
- hf := st.wantHeaders()
- if hf.StreamEnded() {
- t.Fatal("unexpected END_STREAM flag")
- }
- if !hf.HeadersEnded() {
- t.Fatal("want END_HEADERS flag")
- }
-
- df := st.wantData()
- if got := len(df.Data()); got != 0 {
- t.Fatalf("unexpected %d DATA bytes; want 0", got)
- }
- if !df.StreamEnded() {
- t.Fatal("DATA didn't have END_STREAM")
- }
- })
-}
-
-func TestServer_Response_Automatic100Continue(t *testing.T) {
- const msg = "foo"
- const reply = "bar"
- testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
- if v := r.Header.Get("Expect"); v != "" {
- t.Errorf("Expect header = %q; want empty", v)
- }
- buf := make([]byte, len(msg))
- // This read should trigger the 100-continue being sent.
- if n, err := io.ReadFull(r.Body, buf); err != nil || n != len(msg) || string(buf) != msg {
- return fmt.Errorf("ReadFull = %q, %v; want %q, nil", buf[:n], err, msg)
- }
- _, err := io.WriteString(w, reply)
- return err
- }, func(st *serverTester) {
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1, // clients send odd numbers
- BlockFragment: st.encodeHeader(":method", "POST", "expect", "100-continue"),
- EndStream: false,
- EndHeaders: true,
- })
- hf := st.wantHeaders()
- if hf.StreamEnded() {
- t.Fatal("unexpected END_STREAM flag")
- }
- if !hf.HeadersEnded() {
- t.Fatal("want END_HEADERS flag")
- }
- goth := st.decodeHeader(hf.HeaderBlockFragment())
- wanth := [][2]string{
- {":status", "100"},
- }
- if !reflect.DeepEqual(goth, wanth) {
- t.Fatalf("Got headers %v; want %v", goth, wanth)
- }
-
- // Okay, they sent status 100, so we can send our
- // gigantic and/or sensitive "foo" payload now.
- st.writeData(1, true, []byte(msg))
-
- st.wantWindowUpdate(0, uint32(len(msg)))
-
- hf = st.wantHeaders()
- if hf.StreamEnded() {
- t.Fatal("expected data to follow")
- }
- if !hf.HeadersEnded() {
- t.Fatal("want END_HEADERS flag")
- }
- goth = st.decodeHeader(hf.HeaderBlockFragment())
- wanth = [][2]string{
- {":status", "200"},
- {"content-type", "text/plain; charset=utf-8"},
- {"content-length", strconv.Itoa(len(reply))},
- }
- if !reflect.DeepEqual(goth, wanth) {
- t.Errorf("Got headers %v; want %v", goth, wanth)
- }
-
- df := st.wantData()
- if string(df.Data()) != reply {
- t.Errorf("Client read %q; want %q", df.Data(), reply)
- }
- if !df.StreamEnded() {
- t.Errorf("expect data stream end")
- }
- })
-}
-
-func TestServer_HandlerWriteErrorOnDisconnect(t *testing.T) {
- errc := make(chan error, 1)
- testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
- p := []byte("some data.\n")
- for {
- _, err := w.Write(p)
- if err != nil {
- errc <- err
- return nil
- }
- }
- }, func(st *serverTester) {
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1,
- BlockFragment: st.encodeHeader(),
- EndStream: false,
- EndHeaders: true,
- })
- hf := st.wantHeaders()
- if hf.StreamEnded() {
- t.Fatal("unexpected END_STREAM flag")
- }
- if !hf.HeadersEnded() {
- t.Fatal("want END_HEADERS flag")
- }
- // Close the connection and wait for the handler to (hopefully) notice.
- st.cc.Close()
- select {
- case <-errc:
- case <-time.After(5 * time.Second):
- t.Error("timeout")
- }
- })
-}
-
-func TestServer_Rejects_Too_Many_Streams(t *testing.T) {
- const testPath = "/some/path"
-
- inHandler := make(chan uint32)
- leaveHandler := make(chan bool)
- st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
- id := w.(*responseWriter).rws.stream.id
- inHandler <- id
- if id == 1+(defaultMaxStreams+1)*2 && r.URL.Path != testPath {
- t.Errorf("decoded final path as %q; want %q", r.URL.Path, testPath)
- }
- <-leaveHandler
- })
- defer st.Close()
- st.greet()
- nextStreamID := uint32(1)
- streamID := func() uint32 {
- defer func() { nextStreamID += 2 }()
- return nextStreamID
- }
- sendReq := func(id uint32, headers ...string) {
- st.writeHeaders(HeadersFrameParam{
- StreamID: id,
- BlockFragment: st.encodeHeader(headers...),
- EndStream: true,
- EndHeaders: true,
- })
- }
- for i := 0; i < defaultMaxStreams; i++ {
- sendReq(streamID())
- <-inHandler
- }
- defer func() {
- for i := 0; i < defaultMaxStreams; i++ {
- leaveHandler <- true
- }
- }()
-
- // And this one should cross the limit:
- // (It's also sent as a CONTINUATION, to verify we still track the decoder context,
- // even if we're rejecting it)
- rejectID := streamID()
- headerBlock := st.encodeHeader(":path", testPath)
- frag1, frag2 := headerBlock[:3], headerBlock[3:]
- st.writeHeaders(HeadersFrameParam{
- StreamID: rejectID,
- BlockFragment: frag1,
- EndStream: true,
- EndHeaders: false, // CONTINUATION coming
- })
- if err := st.fr.WriteContinuation(rejectID, true, frag2); err != nil {
- t.Fatal(err)
- }
- st.wantRSTStream(rejectID, ErrCodeProtocol)
-
- // But let a handler finish:
- leaveHandler <- true
- st.wantHeaders()
-
- // And now another stream should be able to start:
- goodID := streamID()
- sendReq(goodID, ":path", testPath)
- select {
- case got := <-inHandler:
- if got != goodID {
- t.Errorf("Got stream %d; want %d", got, goodID)
- }
- case <-time.After(3 * time.Second):
- t.Error("timeout waiting for handler")
- }
-}
-
-// So many response headers that the server needs to use CONTINUATION frames:
-func TestServer_Response_ManyHeaders_With_Continuation(t *testing.T) {
- testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
- h := w.Header()
- for i := 0; i < 5000; i++ {
- h.Set(fmt.Sprintf("x-header-%d", i), fmt.Sprintf("x-value-%d", i))
- }
- return nil
- }, func(st *serverTester) {
- getSlash(st)
- hf := st.wantHeaders()
- if hf.HeadersEnded() {
- t.Fatal("got unwanted END_HEADERS flag")
- }
- n := 0
- for {
- n++
- cf := st.wantContinuation()
- if cf.HeadersEnded() {
- break
- }
- }
- if n < 5 {
- t.Errorf("Only got %d CONTINUATION frames; expected 5+ (currently 6)", n)
- }
- })
-}
-
-// This previously crashed (reported by Mathieu Lonjaret as observed
-// while using Camlistore) because we got a DATA frame from the client
-// after the handler exited and our logic at the time was wrong,
-// keeping a stream in the map in stateClosed, which tickled an
-// invariant check later when we tried to remove that stream (via
-// defer sc.closeAllStreamsOnConnClose) when the serverConn serve loop
-// ended.
-func TestServer_NoCrash_HandlerClose_Then_ClientClose(t *testing.T) {
- testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
- // nothing
- return nil
- }, func(st *serverTester) {
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1,
- BlockFragment: st.encodeHeader(),
- EndStream: false, // DATA is coming
- EndHeaders: true,
- })
- hf := st.wantHeaders()
- if !hf.HeadersEnded() || !hf.StreamEnded() {
- t.Fatalf("want END_HEADERS+END_STREAM, got %v", hf)
- }
-
- // Sent when the a Handler closes while a client has
- // indicated it's still sending DATA:
- st.wantRSTStream(1, ErrCodeCancel)
-
- // Now the handler has ended, so it's ended its
- // stream, but the client hasn't closed its side
- // (stateClosedLocal). So send more data and verify
- // it doesn't crash with an internal invariant panic, like
- // it did before.
- st.writeData(1, true, []byte("foo"))
-
- // Sent after a peer sends data anyway (admittedly the
- // previous RST_STREAM might've still been in-flight),
- // but they'll get the more friendly 'cancel' code
- // first.
- st.wantRSTStream(1, ErrCodeStreamClosed)
-
- // Set up a bunch of machinery to record the panic we saw
- // previously.
- var (
- panMu sync.Mutex
- panicVal interface{}
- )
-
- testHookOnPanicMu.Lock()
- testHookOnPanic = func(sc *serverConn, pv interface{}) bool {
- panMu.Lock()
- panicVal = pv
- panMu.Unlock()
- return true
- }
- testHookOnPanicMu.Unlock()
-
- // Now force the serve loop to end, via closing the connection.
- st.cc.Close()
- select {
- case <-st.sc.doneServing:
- // Loop has exited.
- panMu.Lock()
- got := panicVal
- panMu.Unlock()
- if got != nil {
- t.Errorf("Got panic: %v", got)
- }
- case <-time.After(5 * time.Second):
- t.Error("timeout")
- }
- })
-}
-
-func TestServer_Rejects_TLS10(t *testing.T) { testRejectTLS(t, tls.VersionTLS10) }
-func TestServer_Rejects_TLS11(t *testing.T) { testRejectTLS(t, tls.VersionTLS11) }
-
-func testRejectTLS(t *testing.T, max uint16) {
- st := newServerTester(t, nil, func(c *tls.Config) {
- c.MaxVersion = max
- })
- defer st.Close()
- gf := st.wantGoAway()
- if got, want := gf.ErrCode, ErrCodeInadequateSecurity; got != want {
- t.Errorf("Got error code %v; want %v", got, want)
- }
-}
-
-func TestServer_Rejects_TLSBadCipher(t *testing.T) {
- st := newServerTester(t, nil, func(c *tls.Config) {
- // Only list bad ones:
- c.CipherSuites = []uint16{
- tls.TLS_RSA_WITH_RC4_128_SHA,
- tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
- tls.TLS_RSA_WITH_AES_128_CBC_SHA,
- tls.TLS_RSA_WITH_AES_256_CBC_SHA,
- tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
- tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
- tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
- tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
- tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
- tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
- tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
- }
- })
- defer st.Close()
- gf := st.wantGoAway()
- if got, want := gf.ErrCode, ErrCodeInadequateSecurity; got != want {
- t.Errorf("Got error code %v; want %v", got, want)
- }
-}
-
-func TestServer_Advertises_Common_Cipher(t *testing.T) {
- const requiredSuite = tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- st := newServerTester(t, nil, func(c *tls.Config) {
- // Have the client only support the one required by the spec.
- c.CipherSuites = []uint16{requiredSuite}
- }, func(ts *httptest.Server) {
- var srv *http.Server = ts.Config
- // Have the server configured with no specific cipher suites.
- // This tests that Go's defaults include the required one.
- srv.TLSConfig = nil
- })
- defer st.Close()
- st.greet()
-}
-
-func (st *serverTester) onHeaderField(f hpack.HeaderField) {
- if f.Name == "date" {
- return
- }
- st.decodedHeaders = append(st.decodedHeaders, [2]string{f.Name, f.Value})
-}
-
-func (st *serverTester) decodeHeader(headerBlock []byte) (pairs [][2]string) {
- st.decodedHeaders = nil
- if _, err := st.hpackDec.Write(headerBlock); err != nil {
- st.t.Fatalf("hpack decoding error: %v", err)
- }
- if err := st.hpackDec.Close(); err != nil {
- st.t.Fatalf("hpack decoding error: %v", err)
- }
- return st.decodedHeaders
-}
-
-// testServerResponse sets up an idle HTTP/2 connection and lets you
-// write a single request with writeReq, and then reply to it in some way with the provided handler,
-// and then verify the output with the serverTester again (assuming the handler returns nil)
-func testServerResponse(t testing.TB,
- handler func(http.ResponseWriter, *http.Request) error,
- client func(*serverTester),
-) {
- errc := make(chan error, 1)
- st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
- if r.Body == nil {
- t.Fatal("nil Body")
- }
- errc <- handler(w, r)
- })
- defer st.Close()
-
- donec := make(chan bool)
- go func() {
- defer close(donec)
- st.greet()
- client(st)
- }()
-
- select {
- case <-donec:
- return
- case <-time.After(5 * time.Second):
- t.Fatal("timeout")
- }
-
- select {
- case err := <-errc:
- if err != nil {
- t.Fatalf("Error in handler: %v", err)
- }
- case <-time.After(2 * time.Second):
- t.Error("timeout waiting for handler to finish")
- }
-}
-
-// readBodyHandler returns an http Handler func that reads len(want)
-// bytes from r.Body and fails t if the contents read were not
-// the value of want.
-func readBodyHandler(t *testing.T, want string) func(w http.ResponseWriter, r *http.Request) {
- return func(w http.ResponseWriter, r *http.Request) {
- buf := make([]byte, len(want))
- _, err := io.ReadFull(r.Body, buf)
- if err != nil {
- t.Error(err)
- return
- }
- if string(buf) != want {
- t.Errorf("read %q; want %q", buf, want)
- }
- }
-}
-
-// TestServerWithCurl currently fails, hence the LenientCipherSuites test. See:
-// https://github.com/tatsuhiro-t/nghttp2/issues/140 &
-// http://sourceforge.net/p/curl/bugs/1472/
-func TestServerWithCurl(t *testing.T) { testServerWithCurl(t, false) }
-func TestServerWithCurl_LenientCipherSuites(t *testing.T) { testServerWithCurl(t, true) }
-
-func testServerWithCurl(t *testing.T, permitProhibitedCipherSuites bool) {
- if runtime.GOOS != "linux" {
- t.Skip("skipping Docker test when not on Linux; requires --net which won't work with boot2docker anyway")
- }
- if testing.Short() {
- t.Skip("skipping curl test in short mode")
- }
- requireCurl(t)
- var gotConn int32
- testHookOnConn = func() { atomic.StoreInt32(&gotConn, 1) }
-
- const msg = "Hello from curl!\n"
- ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Foo", "Bar")
- w.Header().Set("Client-Proto", r.Proto)
- io.WriteString(w, msg)
- }))
- ConfigureServer(ts.Config, &Server{
- PermitProhibitedCipherSuites: permitProhibitedCipherSuites,
- })
- ts.TLS = ts.Config.TLSConfig // the httptest.Server has its own copy of this TLS config
- ts.StartTLS()
- defer ts.Close()
-
- t.Logf("Running test server for curl to hit at: %s", ts.URL)
- container := curl(t, "--silent", "--http2", "--insecure", "-v", ts.URL)
- defer kill(container)
- resc := make(chan interface{}, 1)
- go func() {
- res, err := dockerLogs(container)
- if err != nil {
- resc <- err
- } else {
- resc <- res
- }
- }()
- select {
- case res := <-resc:
- if err, ok := res.(error); ok {
- t.Fatal(err)
- }
- body := string(res.([]byte))
- // Search for both "key: value" and "key:value", since curl changed their format
- // Our Dockerfile contains the latest version (no space), but just in case people
- // didn't rebuild, check both.
- if !strings.Contains(body, "foo: Bar") && !strings.Contains(body, "foo:Bar") {
- t.Errorf("didn't see foo: Bar header")
- t.Logf("Got: %s", body)
- }
- if !strings.Contains(body, "client-proto: HTTP/2") && !strings.Contains(body, "client-proto:HTTP/2") {
- t.Errorf("didn't see client-proto: HTTP/2 header")
- t.Logf("Got: %s", res)
- }
- if !strings.Contains(string(res.([]byte)), msg) {
- t.Errorf("didn't see %q content", msg)
- t.Logf("Got: %s", res)
- }
- case <-time.After(3 * time.Second):
- t.Errorf("timeout waiting for curl")
- }
-
- if atomic.LoadInt32(&gotConn) == 0 {
- t.Error("never saw an http2 connection")
- }
-}
-
-var doh2load = flag.Bool("h2load", false, "Run h2load test")
-
-func TestServerWithH2Load(t *testing.T) {
- if !*doh2load {
- t.Skip("Skipping without --h2load flag.")
- }
- if runtime.GOOS != "linux" {
- t.Skip("skipping Docker test when not on Linux; requires --net which won't work with boot2docker anyway")
- }
- requireH2load(t)
-
- msg := strings.Repeat("Hello, h2load!\n", 5000)
- ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- io.WriteString(w, msg)
- w.(http.Flusher).Flush()
- io.WriteString(w, msg)
- }))
- ts.StartTLS()
- defer ts.Close()
-
- cmd := exec.Command("docker", "run", "--net=host", "--entrypoint=/usr/local/bin/h2load", "gohttp2/curl",
- "-n100000", "-c100", "-m100", ts.URL)
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
- if err := cmd.Run(); err != nil {
- t.Fatal(err)
- }
-}
-
-// Issue 12843
-func TestServerDoS_MaxHeaderListSize(t *testing.T) {
- st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {})
- defer st.Close()
-
- // shake hands
- st.writePreface()
- st.writeInitialSettings()
- frameSize := defaultMaxReadFrameSize
- var advHeaderListSize *uint32
- st.wantSettings().ForeachSetting(func(s Setting) error {
- switch s.ID {
- case SettingMaxFrameSize:
- if s.Val < minMaxFrameSize {
- frameSize = minMaxFrameSize
- } else if s.Val > maxFrameSize {
- frameSize = maxFrameSize
- } else {
- frameSize = int(s.Val)
- }
- case SettingMaxHeaderListSize:
- advHeaderListSize = &s.Val
- }
- return nil
- })
- st.writeSettingsAck()
- st.wantSettingsAck()
-
- if advHeaderListSize == nil {
- t.Errorf("server didn't advertise a max header list size")
- } else if *advHeaderListSize == 0 {
- t.Errorf("server advertised a max header list size of 0")
- }
-
- st.encodeHeaderField(":method", "GET")
- st.encodeHeaderField(":path", "/")
- st.encodeHeaderField(":scheme", "https")
- cookie := strings.Repeat("*", 4058)
- st.encodeHeaderField("cookie", cookie)
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1,
- BlockFragment: st.headerBuf.Bytes(),
- EndStream: true,
- EndHeaders: false,
- })
-
- // Capture the short encoding of a duplicate ~4K cookie, now
- // that we've already sent it once.
- st.headerBuf.Reset()
- st.encodeHeaderField("cookie", cookie)
-
- // Now send 1MB of it.
- const size = 1 << 20
- b := bytes.Repeat(st.headerBuf.Bytes(), size/st.headerBuf.Len())
- for len(b) > 0 {
- chunk := b
- if len(chunk) > frameSize {
- chunk = chunk[:frameSize]
- }
- b = b[len(chunk):]
- st.fr.WriteContinuation(1, len(b) == 0, chunk)
- }
-
- h := st.wantHeaders()
- if !h.HeadersEnded() {
- t.Fatalf("Got HEADERS without END_HEADERS set: %v", h)
- }
- headers := st.decodeHeader(h.HeaderBlockFragment())
- want := [][2]string{
- {":status", "431"},
- {"content-type", "text/html; charset=utf-8"},
- {"content-length", "63"},
- }
- if !reflect.DeepEqual(headers, want) {
- t.Errorf("Headers mismatch.\n got: %q\nwant: %q\n", headers, want)
- }
-}
-
-func TestCompressionErrorOnWrite(t *testing.T) {
- const maxStrLen = 8 << 10
- var serverConfig *http.Server
- st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
- // No response body.
- }, func(ts *httptest.Server) {
- serverConfig = ts.Config
- serverConfig.MaxHeaderBytes = maxStrLen
- })
- st.addLogFilter("connection error: COMPRESSION_ERROR")
- defer st.Close()
- st.greet()
-
- maxAllowed := st.sc.maxHeaderStringLen()
-
- // Crank this up, now that we have a conn connected with the
- // hpack.Decoder's max string length set has been initialized
- // from the earlier low ~8K value. We want this higher so don't
- // hit the max header list size. We only want to test hitting
- // the max string size.
- serverConfig.MaxHeaderBytes = 1 << 20
-
- // First a request with a header that's exactly the max allowed size.
- hbf := st.encodeHeader("foo", strings.Repeat("a", maxAllowed))
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1,
- BlockFragment: hbf,
- EndStream: true,
- EndHeaders: true,
- })
- h := st.wantHeaders()
- if !h.HeadersEnded() || !h.StreamEnded() {
- t.Errorf("Unexpected HEADER frame %v", h)
- }
-
- // And now send one that's just one byte too big.
- hbf = st.encodeHeader("bar", strings.Repeat("b", maxAllowed+1))
- st.writeHeaders(HeadersFrameParam{
- StreamID: 3,
- BlockFragment: hbf,
- EndStream: true,
- EndHeaders: true,
- })
- ga := st.wantGoAway()
- if ga.ErrCode != ErrCodeCompression {
- t.Errorf("GOAWAY err = %v; want ErrCodeCompression", ga.ErrCode)
- }
-}
-
-func TestCompressionErrorOnClose(t *testing.T) {
- st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
- // No response body.
- })
- st.addLogFilter("connection error: COMPRESSION_ERROR")
- defer st.Close()
- st.greet()
-
- hbf := st.encodeHeader("foo", "bar")
- hbf = hbf[:len(hbf)-1] // truncate one byte from the end, so hpack.Decoder.Close fails.
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1,
- BlockFragment: hbf,
- EndStream: true,
- EndHeaders: true,
- })
- ga := st.wantGoAway()
- if ga.ErrCode != ErrCodeCompression {
- t.Errorf("GOAWAY err = %v; want ErrCodeCompression", ga.ErrCode)
- }
-}
-
-// test that a server handler can read trailers from a client
-func TestServerReadsTrailers(t *testing.T) {
- const testBody = "some test body"
- writeReq := func(st *serverTester) {
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1, // clients send odd numbers
- BlockFragment: st.encodeHeader("trailer", "Foo, Bar", "trailer", "Baz"),
- EndStream: false,
- EndHeaders: true,
- })
- st.writeData(1, false, []byte(testBody))
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1, // clients send odd numbers
- BlockFragment: st.encodeHeaderRaw(
- "foo", "foov",
- "bar", "barv",
- "baz", "bazv",
- "surprise", "wasn't declared; shouldn't show up",
- ),
- EndStream: true,
- EndHeaders: true,
- })
- }
- checkReq := func(r *http.Request) {
- wantTrailer := http.Header{
- "Foo": nil,
- "Bar": nil,
- "Baz": nil,
- }
- if !reflect.DeepEqual(r.Trailer, wantTrailer) {
- t.Errorf("initial Trailer = %v; want %v", r.Trailer, wantTrailer)
- }
- slurp, err := ioutil.ReadAll(r.Body)
- if string(slurp) != testBody {
- t.Errorf("read body %q; want %q", slurp, testBody)
- }
- if err != nil {
- t.Fatalf("Body slurp: %v", err)
- }
- wantTrailerAfter := http.Header{
- "Foo": {"foov"},
- "Bar": {"barv"},
- "Baz": {"bazv"},
- }
- if !reflect.DeepEqual(r.Trailer, wantTrailerAfter) {
- t.Errorf("final Trailer = %v; want %v", r.Trailer, wantTrailerAfter)
- }
- }
- testServerRequest(t, writeReq, checkReq)
-}
-
-// test that a server handler can send trailers
-func TestServerWritesTrailers_WithFlush(t *testing.T) { testServerWritesTrailers(t, true) }
-func TestServerWritesTrailers_WithoutFlush(t *testing.T) { testServerWritesTrailers(t, false) }
-
-func testServerWritesTrailers(t *testing.T, withFlush bool) {
- // See https://httpwg.github.io/specs/rfc7540.html#rfc.section.8.1.3
- testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
- w.Header().Set("Trailer", "Server-Trailer-A, Server-Trailer-B")
- w.Header().Add("Trailer", "Server-Trailer-C")
-
- // TODO: decide if the server should filter these while
- // writing the Trailer header in the response. Currently it
- // appears net/http doesn't do this for http/1.1
- w.Header().Add("Trailer", "Transfer-Encoding, Content-Length, Trailer") // filtered
- w.Header().Set("Foo", "Bar")
- w.Header().Set("Content-Length", "5")
-
- io.WriteString(w, "Hello")
- if withFlush {
- w.(http.Flusher).Flush()
- }
- w.Header().Set("Server-Trailer-A", "valuea")
- w.Header().Set("Server-Trailer-C", "valuec") // skipping B
- w.Header().Set("Server-Surpise", "surprise! this isn't predeclared!")
- w.Header().Set("Transfer-Encoding", "should not be included; Forbidden by RFC 2616 14.40")
- w.Header().Set("Content-Length", "should not be included; Forbidden by RFC 2616 14.40")
- w.Header().Set("Trailer", "should not be included; Forbidden by RFC 2616 14.40")
- return nil
- }, func(st *serverTester) {
- getSlash(st)
- hf := st.wantHeaders()
- if hf.StreamEnded() {
- t.Fatal("response HEADERS had END_STREAM")
- }
- if !hf.HeadersEnded() {
- t.Fatal("response HEADERS didn't have END_HEADERS")
- }
- goth := st.decodeHeader(hf.HeaderBlockFragment())
- wanth := [][2]string{
- {":status", "200"},
- {"foo", "Bar"},
- {"trailer", "Server-Trailer-A, Server-Trailer-B"},
- {"trailer", "Server-Trailer-C"},
- {"trailer", "Transfer-Encoding, Content-Length, Trailer"},
- {"content-type", "text/plain; charset=utf-8"},
- {"content-length", "5"},
- }
- if !reflect.DeepEqual(goth, wanth) {
- t.Errorf("Header mismatch.\n got: %v\nwant: %v", goth, wanth)
- }
- df := st.wantData()
- if string(df.Data()) != "Hello" {
- t.Fatalf("Client read %q; want Hello", df.Data())
- }
- if df.StreamEnded() {
- t.Fatalf("data frame had STREAM_ENDED")
- }
- tf := st.wantHeaders() // for the trailers
- if !tf.StreamEnded() {
- t.Fatalf("trailers HEADERS lacked END_STREAM")
- }
- if !tf.HeadersEnded() {
- t.Fatalf("trailers HEADERS lacked END_HEADERS")
- }
- wanth = [][2]string{
- {"server-trailer-a", "valuea"},
- {"server-trailer-c", "valuec"},
- }
- goth = st.decodeHeader(tf.HeaderBlockFragment())
- if !reflect.DeepEqual(goth, wanth) {
- t.Errorf("Header mismatch.\n got: %v\nwant: %v", goth, wanth)
- }
- })
-}
-
-func BenchmarkServerGets(b *testing.B) {
- b.ReportAllocs()
-
- const msg = "Hello, world"
- st := newServerTester(b, func(w http.ResponseWriter, r *http.Request) {
- io.WriteString(w, msg)
- })
- defer st.Close()
- st.greet()
-
- // Give the server quota to reply. (plus it has the the 64KB)
- if err := st.fr.WriteWindowUpdate(0, uint32(b.N*len(msg))); err != nil {
- b.Fatal(err)
- }
-
- for i := 0; i < b.N; i++ {
- id := 1 + uint32(i)*2
- st.writeHeaders(HeadersFrameParam{
- StreamID: id,
- BlockFragment: st.encodeHeader(),
- EndStream: true,
- EndHeaders: true,
- })
- st.wantHeaders()
- df := st.wantData()
- if !df.StreamEnded() {
- b.Fatalf("DATA didn't have END_STREAM; got %v", df)
- }
- }
-}
-
-func BenchmarkServerPosts(b *testing.B) {
- b.ReportAllocs()
-
- const msg = "Hello, world"
- st := newServerTester(b, func(w http.ResponseWriter, r *http.Request) {
- io.WriteString(w, msg)
- })
- defer st.Close()
- st.greet()
-
- // Give the server quota to reply. (plus it has the the 64KB)
- if err := st.fr.WriteWindowUpdate(0, uint32(b.N*len(msg))); err != nil {
- b.Fatal(err)
- }
-
- for i := 0; i < b.N; i++ {
- id := 1 + uint32(i)*2
- st.writeHeaders(HeadersFrameParam{
- StreamID: id,
- BlockFragment: st.encodeHeader(":method", "POST"),
- EndStream: false,
- EndHeaders: true,
- })
- st.writeData(id, true, nil)
- st.wantHeaders()
- df := st.wantData()
- if !df.StreamEnded() {
- b.Fatalf("DATA didn't have END_STREAM; got %v", df)
- }
- }
-}
-
-// go-fuzz bug, originally reported at https://github.com/bradfitz/http2/issues/53
-// Verify we don't hang.
-func TestIssue53(t *testing.T) {
- const data = "PRI * HTTP/2.0\r\n\r\nSM" +
- "\r\n\r\n\x00\x00\x00\x01\ainfinfin\ad"
- s := &http.Server{
- ErrorLog: log.New(io.MultiWriter(stderrv(), twriter{t: t}), "", log.LstdFlags),
- }
- s2 := &Server{MaxReadFrameSize: 1 << 16, PermitProhibitedCipherSuites: true}
- c := &issue53Conn{[]byte(data), false, false}
- s2.handleConn(s, c, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
- w.Write([]byte("hello"))
- }))
- if !c.closed {
- t.Fatal("connection is not closed")
- }
-}
-
-type issue53Conn struct {
- data []byte
- closed bool
- written bool
-}
-
-func (c *issue53Conn) Read(b []byte) (n int, err error) {
- if len(c.data) == 0 {
- return 0, io.EOF
- }
- n = copy(b, c.data)
- c.data = c.data[n:]
- return
-}
-
-func (c *issue53Conn) Write(b []byte) (n int, err error) {
- c.written = true
- return len(b), nil
-}
-
-func (c *issue53Conn) Close() error {
- c.closed = true
- return nil
-}
-
-func (c *issue53Conn) LocalAddr() net.Addr { return &net.TCPAddr{net.IP{127, 0, 0, 1}, 49706, ""} }
-func (c *issue53Conn) RemoteAddr() net.Addr { return &net.TCPAddr{net.IP{127, 0, 0, 1}, 49706, ""} }
-func (c *issue53Conn) SetDeadline(t time.Time) error { return nil }
-func (c *issue53Conn) SetReadDeadline(t time.Time) error { return nil }
-func (c *issue53Conn) SetWriteDeadline(t time.Time) error { return nil }
-
-// golang.org/issue/12895
-func TestConfigureServer(t *testing.T) {
- tests := []struct {
- name string
- in http.Server
- wantErr string
- }{
- {
- name: "empty server",
- in: http.Server{},
- },
- {
- name: "just the required cipher suite",
- in: http.Server{
- TLSConfig: &tls.Config{
- CipherSuites: []uint16{tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
- },
- },
- },
- {
- name: "missing required cipher suite",
- in: http.Server{
- TLSConfig: &tls.Config{
- CipherSuites: []uint16{tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
- },
- },
- wantErr: "is missing HTTP/2-required TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
- },
- {
- name: "required after bad",
- in: http.Server{
- TLSConfig: &tls.Config{
- CipherSuites: []uint16{tls.TLS_RSA_WITH_RC4_128_SHA, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
- },
- },
- wantErr: "contains an HTTP/2-approved cipher suite (0xc02f), but it comes after",
- },
- {
- name: "bad after required",
- in: http.Server{
- TLSConfig: &tls.Config{
- CipherSuites: []uint16{tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_RSA_WITH_RC4_128_SHA},
- },
- },
- },
- }
- for _, tt := range tests {
- err := ConfigureServer(&tt.in, nil)
- if (err != nil) != (tt.wantErr != "") {
- if tt.wantErr != "" {
- t.Errorf("%s: success, but want error", tt.name)
- } else {
- t.Errorf("%s: unexpected error: %v", tt.name, err)
- }
- }
- if err != nil && tt.wantErr != "" && !strings.Contains(err.Error(), tt.wantErr) {
- t.Errorf("%s: err = %v; want substring %q", tt.name, err, tt.wantErr)
- }
- if err == nil && !tt.in.TLSConfig.PreferServerCipherSuites {
- t.Errorf("%s: PreferServerCipherSuite is false; want true", tt.name)
- }
- }
-}
-
-func TestServerRejectHeadWithBody(t *testing.T) {
- st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
- // No response body.
- })
- defer st.Close()
- st.greet()
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1, // clients send odd numbers
- BlockFragment: st.encodeHeader(":method", "HEAD"),
- EndStream: false, // what we're testing, a bogus HEAD request with body
- EndHeaders: true,
- })
- st.wantRSTStream(1, ErrCodeProtocol)
-}
-
-func TestServerNoAutoContentLengthOnHead(t *testing.T) {
- st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
- // No response body. (or smaller than one frame)
- })
- defer st.Close()
- st.greet()
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1, // clients send odd numbers
- BlockFragment: st.encodeHeader(":method", "HEAD"),
- EndStream: true,
- EndHeaders: true,
- })
- h := st.wantHeaders()
- headers := st.decodeHeader(h.HeaderBlockFragment())
- want := [][2]string{
- {":status", "200"},
- {"content-type", "text/plain; charset=utf-8"},
- }
- if !reflect.DeepEqual(headers, want) {
- t.Errorf("Headers mismatch.\n got: %q\nwant: %q\n", headers, want)
- }
-}
-
-// golang.org/issue/13495
-func TestServerNoDuplicateContentType(t *testing.T) {
- st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
- w.Header()["Content-Type"] = []string{""}
- fmt.Fprintf(w, "
hi")
- })
- defer st.Close()
- st.greet()
- st.writeHeaders(HeadersFrameParam{
- StreamID: 1,
- BlockFragment: st.encodeHeader(),
- EndStream: true,
- EndHeaders: true,
- })
- h := st.wantHeaders()
- headers := st.decodeHeader(h.HeaderBlockFragment())
- want := [][2]string{
- {":status", "200"},
- {"content-type", ""},
- {"content-length", "41"},
- }
- if !reflect.DeepEqual(headers, want) {
- t.Errorf("Headers mismatch.\n got: %q\nwant: %q\n", headers, want)
- }
-}
diff --git a/Godeps/_workspace/src/golang.org/x/net/http2/testdata/draft-ietf-httpbis-http2.xml b/Godeps/_workspace/src/golang.org/x/net/http2/testdata/draft-ietf-httpbis-http2.xml
deleted file mode 100644
index 31a84bed4f0..00000000000
--- a/Godeps/_workspace/src/golang.org/x/net/http2/testdata/draft-ietf-httpbis-http2.xml
+++ /dev/null
@@ -1,5021 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Hypertext Transfer Protocol version 2
-
-
- Twist
-
- mbelshe@chromium.org
-
-
-
-
- Google, Inc
-
- fenix@google.com
-
-
-
-
- Mozilla
-
-
- 331 E Evelyn Street
- Mountain View
- CA
- 94041
- US
-
- martin.thomson@gmail.com
-
-
-
-
- Applications
- HTTPbis
- HTTP
- SPDY
- Web
-
-
-
- This specification describes an optimized expression of the semantics of the Hypertext
- Transfer Protocol (HTTP). HTTP/2 enables a more efficient use of network resources and a
- reduced perception of latency by introducing header field compression and allowing multiple
- concurrent messages on the same connection. It also introduces unsolicited push of
- representations from servers to clients.
-
-
- This specification is an alternative to, but does not obsolete, the HTTP/1.1 message syntax.
- HTTP's existing semantics remain unchanged.
-
-
-
-
-
- Discussion of this draft takes place on the HTTPBIS working group mailing list
- (ietf-http-wg@w3.org), which is archived at .
-
-
- Working Group information can be found at ; that specific to HTTP/2 are at .
-
-
- The changes in this draft are summarized in .
-
-
-
-
-
-
-
-
-
- The Hypertext Transfer Protocol (HTTP) is a wildly successful protocol. However, the
- HTTP/1.1 message format () has
- several characteristics that have a negative overall effect on application performance
- today.
-
-
- In particular, HTTP/1.0 allowed only one request to be outstanding at a time on a given
- TCP connection. HTTP/1.1 added request pipelining, but this only partially addressed
- request concurrency and still suffers from head-of-line blocking. Therefore, HTTP/1.1
- clients that need to make many requests typically use multiple connections to a server in
- order to achieve concurrency and thereby reduce latency.
-
-
- Furthermore, HTTP header fields are often repetitive and verbose, causing unnecessary
- network traffic, as well as causing the initial TCP congestion
- window to quickly fill. This can result in excessive latency when multiple requests are
- made on a new TCP connection.
-
-
- HTTP/2 addresses these issues by defining an optimized mapping of HTTP's semantics to an
- underlying connection. Specifically, it allows interleaving of request and response
- messages on the same connection and uses an efficient coding for HTTP header fields. It
- also allows prioritization of requests, letting more important requests complete more
- quickly, further improving performance.
-
-
- The resulting protocol is more friendly to the network, because fewer TCP connections can
- be used in comparison to HTTP/1.x. This means less competition with other flows, and
- longer-lived connections, which in turn leads to better utilization of available network
- capacity.
-
-
- Finally, HTTP/2 also enables more efficient processing of messages through use of binary
- message framing.
-
-
-
-
-
- HTTP/2 provides an optimized transport for HTTP semantics. HTTP/2 supports all of the core
- features of HTTP/1.1, but aims to be more efficient in several ways.
-
-
- The basic protocol unit in HTTP/2 is a frame. Each frame
- type serves a different purpose. For example, HEADERS and
- DATA frames form the basis of HTTP requests and
- responses; other frame types like SETTINGS,
- WINDOW_UPDATE, and PUSH_PROMISE are used in support of other
- HTTP/2 features.
-
-
- Multiplexing of requests is achieved by having each HTTP request-response exchange
- associated with its own stream. Streams are largely
- independent of each other, so a blocked or stalled request or response does not prevent
- progress on other streams.
-
-
- Flow control and prioritization ensure that it is possible to efficiently use multiplexed
- streams. Flow control helps to ensure that only data that
- can be used by a receiver is transmitted. Prioritization ensures that limited resources can be directed
- to the most important streams first.
-
-
- HTTP/2 adds a new interaction mode, whereby a server can push
- responses to a client. Server push allows a server to speculatively send a client
- data that the server anticipates the client will need, trading off some network usage
- against a potential latency gain. The server does this by synthesizing a request, which it
- sends as a PUSH_PROMISE frame. The server is then able to send a response to
- the synthetic request on a separate stream.
-
-
- Frames that contain HTTP header fields are compressed.
- HTTP requests can be highly redundant, so compression can reduce the size of requests and
- responses significantly.
-
-
-
-
- The HTTP/2 specification is split into four parts:
-
-
- Starting HTTP/2 covers how an HTTP/2 connection is
- initiated.
-
-
- The framing and streams layers describe the way HTTP/2 frames are
- structured and formed into multiplexed streams.
-
-
- Frame and error
- definitions include details of the frame and error types used in HTTP/2.
-
-
- HTTP mappings and additional
- requirements describe how HTTP semantics are expressed using frames and
- streams.
-
-
-
-
- While some of the frame and stream layer concepts are isolated from HTTP, this
- specification does not define a completely generic framing layer. The framing and streams
- layers are tailored to the needs of the HTTP protocol and server push.
-
-
-
-
-
- The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD
- NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as
- described in RFC 2119.
-
-
- All numeric values are in network byte order. Values are unsigned unless otherwise
- indicated. Literal values are provided in decimal or hexadecimal as appropriate.
- Hexadecimal literals are prefixed with 0x to distinguish them
- from decimal literals.
-
-
- The following terms are used:
-
-
- The endpoint initiating the HTTP/2 connection.
-
-
- A transport-layer connection between two endpoints.
-
-
- An error that affects the entire HTTP/2 connection.
-
-
- Either the client or server of the connection.
-
-
- The smallest unit of communication within an HTTP/2 connection, consisting of a header
- and a variable-length sequence of octets structured according to the frame type.
-
-
- An endpoint. When discussing a particular endpoint, "peer" refers to the endpoint
- that is remote to the primary subject of discussion.
-
-
- An endpoint that is receiving frames.
-
-
- An endpoint that is transmitting frames.
-
-
- The endpoint which did not initiate the HTTP/2 connection.
-
-
- A bi-directional flow of frames across a virtual channel within the HTTP/2 connection.
-
-
- An error on the individual HTTP/2 stream.
-
-
-
-
- Finally, the terms "gateway", "intermediary", "proxy", and "tunnel" are defined
- in .
-
-
-
-
-
-
- An HTTP/2 connection is an application layer protocol running on top of a TCP connection
- (). The client is the TCP connection initiator.
-
-
- HTTP/2 uses the same "http" and "https" URI schemes used by HTTP/1.1. HTTP/2 shares the same
- default port numbers: 80 for "http" URIs and 443 for "https" URIs. As a result,
- implementations processing requests for target resource URIs like http://example.org/foo or https://example.com/bar are required to first discover whether the
- upstream server (the immediate peer to which the client wishes to establish a connection)
- supports HTTP/2.
-
-
-
- The means by which support for HTTP/2 is determined is different for "http" and "https"
- URIs. Discovery for "http" URIs is described in . Discovery
- for "https" URIs is described in .
-
-
-
-
- The protocol defined in this document has two identifiers.
-
-
-
- The string "h2" identifies the protocol where HTTP/2 uses TLS. This identifier is used in the TLS application layer protocol negotiation extension (ALPN)
- field and any place that HTTP/2 over TLS is identified.
-
-
- The "h2" string is serialized into an ALPN protocol identifier as the two octet
- sequence: 0x68, 0x32.
-
-
-
-
- The string "h2c" identifies the protocol where HTTP/2 is run over cleartext TCP.
- This identifier is used in the HTTP/1.1 Upgrade header field and any place that
- HTTP/2 over TCP is identified.
-
-
-
-
-
- Negotiating "h2" or "h2c" implies the use of the transport, security, framing and message
- semantics described in this document.
-
-
- RFC Editor's Note: please remove the remainder of this section prior to the
- publication of a final version of this document.
-
-
- Only implementations of the final, published RFC can identify themselves as "h2" or "h2c".
- Until such an RFC exists, implementations MUST NOT identify themselves using these
- strings.
-
-
- Examples and text throughout the rest of this document use "h2" as a matter of
- editorial convenience only. Implementations of draft versions MUST NOT identify using
- this string.
-
-
- Implementations of draft versions of the protocol MUST add the string "-" and the
- corresponding draft number to the identifier. For example, draft-ietf-httpbis-http2-11
- over TLS is identified using the string "h2-11".
-
-
- Non-compatible experiments that are based on these draft versions MUST append the string
- "-" and an experiment name to the identifier. For example, an experimental implementation
- of packet mood-based encoding based on draft-ietf-httpbis-http2-09 might identify itself
- as "h2-09-emo". Note that any label MUST conform to the "token" syntax defined in
- . Experimenters are
- encouraged to coordinate their experiments on the ietf-http-wg@w3.org mailing list.
-
-
-
-
-
- A client that makes a request for an "http" URI without prior knowledge about support for
- HTTP/2 uses the HTTP Upgrade mechanism (). The client makes an HTTP/1.1 request that includes an Upgrade
- header field identifying HTTP/2 with the "h2c" token. The HTTP/1.1 request MUST include
- exactly one HTTP2-Settings header field.
-
-
- For example:
-
-
-]]>
-
-
- Requests that contain an entity body MUST be sent in their entirety before the client can
- send HTTP/2 frames. This means that a large request entity can block the use of the
- connection until it is completely sent.
-
-
- If concurrency of an initial request with subsequent requests is important, an OPTIONS
- request can be used to perform the upgrade to HTTP/2, at the cost of an additional
- round-trip.
-
-
- A server that does not support HTTP/2 can respond to the request as though the Upgrade
- header field were absent:
-
-
-
-HTTP/1.1 200 OK
-Content-Length: 243
-Content-Type: text/html
-
-...
-
-
-
- A server MUST ignore a "h2" token in an Upgrade header field. Presence of a token with
- "h2" implies HTTP/2 over TLS, which is instead negotiated as described in .
-
-
- A server that supports HTTP/2 can accept the upgrade with a 101 (Switching Protocols)
- response. After the empty line that terminates the 101 response, the server can begin
- sending HTTP/2 frames. These frames MUST include a response to the request that initiated
- the Upgrade.
-
-
-
-
- For example:
-
-
-HTTP/1.1 101 Switching Protocols
-Connection: Upgrade
-Upgrade: h2c
-
-[ HTTP/2 connection ...
-
-
-
- The first HTTP/2 frame sent by the server is a SETTINGS frame () as the server connection preface (). Upon receiving the 101 response, the client sends a connection preface, which includes a
- SETTINGS frame.
-
-
- The HTTP/1.1 request that is sent prior to upgrade is assigned stream identifier 1 and is
- assigned default priority values. Stream 1 is
- implicitly half closed from the client toward the server, since the request is completed
- as an HTTP/1.1 request. After commencing the HTTP/2 connection, stream 1 is used for the
- response.
-
-
-
-
- A request that upgrades from HTTP/1.1 to HTTP/2 MUST include exactly one HTTP2-Settings header field. The HTTP2-Settings header field is a connection-specific header field
- that includes parameters that govern the HTTP/2 connection, provided in anticipation of
- the server accepting the request to upgrade.
-
-
-
-
-
- A server MUST NOT upgrade the connection to HTTP/2 if this header field is not present,
- or if more than one is present. A server MUST NOT send this header field.
-
-
-
- The content of the HTTP2-Settings header field is the
- payload of a SETTINGS frame (), encoded as a
- base64url string (that is, the URL- and filename-safe Base64 encoding described in , with any trailing '=' characters omitted). The
- ABNF production for token68 is
- defined in .
-
-
- Since the upgrade is only intended to apply to the immediate connection, a client
- sending HTTP2-Settings MUST also send HTTP2-Settings as a connection option in the Connection header field to prevent it from being forwarded
- downstream.
-
-
- A server decodes and interprets these values as it would any other
- SETTINGS frame. Acknowledgement of the
- SETTINGS parameters is not necessary, since a 101 response serves as implicit
- acknowledgment. Providing these values in the Upgrade request gives a client an
- opportunity to provide parameters prior to receiving any frames from the server.
-
-
-
-
-
-
- A client that makes a request to an "https" URI uses TLS
- with the application layer protocol negotiation extension.
-
-
- HTTP/2 over TLS uses the "h2" application token. The "h2c" token MUST NOT be sent by a
- client or selected by a server.
-
-
- Once TLS negotiation is complete, both the client and the server send a connection preface.
-
-
-
-
-
- A client can learn that a particular server supports HTTP/2 by other means. For example,
- describes a mechanism for advertising this capability.
-
-
- A client MAY immediately send HTTP/2 frames to a server that is known to support HTTP/2,
- after the connection preface; a server can
- identify such a connection by the presence of the connection preface. This only affects
- the establishment of HTTP/2 connections over cleartext TCP; implementations that support
- HTTP/2 over TLS MUST use protocol negotiation in TLS.
-
-
- Without additional information, prior support for HTTP/2 is not a strong signal that a
- given server will support HTTP/2 for future connections. For example, it is possible for
- server configurations to change, for configurations to differ between instances in
- clustered servers, or for network conditions to change.
-
-
-
-
-
- Upon establishment of a TCP connection and determination that HTTP/2 will be used by both
- peers, each endpoint MUST send a connection preface as a final confirmation and to
- establish the initial SETTINGS parameters for the HTTP/2 connection. The client and
- server each send a different connection preface.
-
-
- The client connection preface starts with a sequence of 24 octets, which in hex notation
- are:
-
-
-
-
-
- (the string PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n). This sequence
- is followed by a SETTINGS frame (). The
- SETTINGS frame MAY be empty. The client sends the client connection
- preface immediately upon receipt of a 101 Switching Protocols response (indicating a
- successful upgrade), or as the first application data octets of a TLS connection. If
- starting an HTTP/2 connection with prior knowledge of server support for the protocol, the
- client connection preface is sent upon connection establishment.
-
-
-
-
- The client connection preface is selected so that a large proportion of HTTP/1.1 or
- HTTP/1.0 servers and intermediaries do not attempt to process further frames. Note
- that this does not address the concerns raised in .
-
-
-
-
- The server connection preface consists of a potentially empty SETTINGS
- frame () that MUST be the first frame the server sends in the
- HTTP/2 connection.
-
-
- The SETTINGS frames received from a peer as part of the connection preface
- MUST be acknowledged (see ) after sending the connection
- preface.
-
-
- To avoid unnecessary latency, clients are permitted to send additional frames to the
- server immediately after sending the client connection preface, without waiting to receive
- the server connection preface. It is important to note, however, that the server
- connection preface SETTINGS frame might include parameters that necessarily
- alter how a client is expected to communicate with the server. Upon receiving the
- SETTINGS frame, the client is expected to honor any parameters established.
- In some configurations, it is possible for the server to transmit SETTINGS
- before the client sends additional frames, providing an opportunity to avoid this issue.
-
-
- Clients and servers MUST treat an invalid connection preface as a connection error of type
- PROTOCOL_ERROR. A GOAWAY frame ()
- MAY be omitted in this case, since an invalid preface indicates that the peer is not using
- HTTP/2.
-
-
-
-
-
-
- Once the HTTP/2 connection is established, endpoints can begin exchanging frames.
-
-
-
-
- All frames begin with a fixed 9-octet header followed by a variable-length payload.
-
-
-
-
-
- The fields of the frame header are defined as:
-
-
-
- The length of the frame payload expressed as an unsigned 24-bit integer. Values
- greater than 214 (16,384) MUST NOT be sent unless the receiver has
- set a larger value for SETTINGS_MAX_FRAME_SIZE.
-
-
- The 9 octets of the frame header are not included in this value.
-
-
-
-
- The 8-bit type of the frame. The frame type determines the format and semantics of
- the frame. Implementations MUST ignore and discard any frame that has a type that
- is unknown.
-
-
-
-
- An 8-bit field reserved for frame-type specific boolean flags.
-
-
- Flags are assigned semantics specific to the indicated frame type. Flags that have
- no defined semantics for a particular frame type MUST be ignored, and MUST be left
- unset (0) when sending.
-
-
-
-
- A reserved 1-bit field. The semantics of this bit are undefined and the bit MUST
- remain unset (0) when sending and MUST be ignored when receiving.
-
-
-
-
- A 31-bit stream identifier (see ). The value 0 is
- reserved for frames that are associated with the connection as a whole as opposed to
- an individual stream.
-
-
-
-
-
- The structure and content of the frame payload is dependent entirely on the frame type.
-
-
-
-
-
- The size of a frame payload is limited by the maximum size that a receiver advertises in
- the SETTINGS_MAX_FRAME_SIZE setting. This setting can have any value
- between 214 (16,384) and 224-1 (16,777,215) octets,
- inclusive.
-
-
- All implementations MUST be capable of receiving and minimally processing frames up to
- 214 octets in length, plus the 9 octet frame
- header. The size of the frame header is not included when describing frame sizes.
-
-
- Certain frame types, such as PING, impose additional limits
- on the amount of payload data allowed.
-
-
-
-
- If a frame size exceeds any defined limit, or is too small to contain mandatory frame
- data, the endpoint MUST send a FRAME_SIZE_ERROR error. A frame size error
- in a frame that could alter the state of the entire connection MUST be treated as a connection error; this includes any frame carrying
- a header block (that is, HEADERS,
- PUSH_PROMISE, and CONTINUATION), SETTINGS,
- and any WINDOW_UPDATE frame with a stream identifier of 0.
-
-
- Endpoints are not obligated to use all available space in a frame. Responsiveness can be
- improved by using frames that are smaller than the permitted maximum size. Sending large
- frames can result in delays in sending time-sensitive frames (such
- RST_STREAM, WINDOW_UPDATE, or PRIORITY)
- which if blocked by the transmission of a large frame, could affect performance.
-
-
-
-
-
- Just as in HTTP/1, a header field in HTTP/2 is a name with one or more associated values.
- They are used within HTTP request and response messages as well as server push operations
- (see ).
-
-
- Header lists are collections of zero or more header fields. When transmitted over a
- connection, a header list is serialized into a header block using HTTP Header Compression. The serialized header block is then
- divided into one or more octet sequences, called header block fragments, and transmitted
- within the payload of HEADERS, PUSH_PROMISE or CONTINUATION frames.
-
-
- The Cookie header field is treated specially by the HTTP
- mapping (see ).
-
-
- A receiving endpoint reassembles the header block by concatenating its fragments, then
- decompresses the block to reconstruct the header list.
-
-
- A complete header block consists of either:
-
-
- a single HEADERS or PUSH_PROMISE frame,
- with the END_HEADERS flag set, or
-
-
- a HEADERS or PUSH_PROMISE frame with the END_HEADERS
- flag cleared and one or more CONTINUATION frames,
- where the last CONTINUATION frame has the END_HEADERS flag set.
-
-
-
-
- Header compression is stateful. One compression context and one decompression context is
- used for the entire connection. Each header block is processed as a discrete unit.
- Header blocks MUST be transmitted as a contiguous sequence of frames, with no interleaved
- frames of any other type or from any other stream. The last frame in a sequence of
- HEADERS or CONTINUATION frames MUST have the END_HEADERS
- flag set. The last frame in a sequence of PUSH_PROMISE or
- CONTINUATION frames MUST have the END_HEADERS flag set. This allows a
- header block to be logically equivalent to a single frame.
-
-
- Header block fragments can only be sent as the payload of HEADERS,
- PUSH_PROMISE or CONTINUATION frames, because these frames
- carry data that can modify the compression context maintained by a receiver. An endpoint
- receiving HEADERS, PUSH_PROMISE or
- CONTINUATION frames MUST reassemble header blocks and perform decompression
- even if the frames are to be discarded. A receiver MUST terminate the connection with a
- connection error of type
- COMPRESSION_ERROR if it does not decompress a header block.
-
-
-
-
-
-
- A "stream" is an independent, bi-directional sequence of frames exchanged between the client
- and server within an HTTP/2 connection. Streams have several important characteristics:
-
-
- A single HTTP/2 connection can contain multiple concurrently open streams, with either
- endpoint interleaving frames from multiple streams.
-
-
- Streams can be established and used unilaterally or shared by either the client or
- server.
-
-
- Streams can be closed by either endpoint.
-
-
- The order in which frames are sent on a stream is significant. Recipients process frames
- in the order they are received. In particular, the order of HEADERS,
- and DATA frames is semantically significant.
-
-
- Streams are identified by an integer. Stream identifiers are assigned to streams by the
- endpoint initiating the stream.
-
-
-
-
-
-
- The lifecycle of a stream is shown in .
-
-
-
-
- | |<-----------' |
- | R | closed | R |
- `-------------------->| |<--------------------'
- +--------+
-
- H: HEADERS frame (with implied CONTINUATIONs)
- PP: PUSH_PROMISE frame (with implied CONTINUATIONs)
- ES: END_STREAM flag
- R: RST_STREAM frame
-]]>
-
-
-
-
- Note that this diagram shows stream state transitions and the frames and flags that affect
- those transitions only. In this regard, CONTINUATION frames do not result
- in state transitions; they are effectively part of the HEADERS or
- PUSH_PROMISE that they follow. For this purpose, the END_STREAM flag is
- processed as a separate event to the frame that bears it; a HEADERS frame
- with the END_STREAM flag set can cause two state transitions.
-
-
- Both endpoints have a subjective view of the state of a stream that could be different
- when frames are in transit. Endpoints do not coordinate the creation of streams; they are
- created unilaterally by either endpoint. The negative consequences of a mismatch in
- states are limited to the "closed" state after sending RST_STREAM, where
- frames might be received for some time after closing.
-
-
- Streams have the following states:
-
-
-
-
-
- All streams start in the "idle" state. In this state, no frames have been
- exchanged.
-
-
- The following transitions are valid from this state:
-
-
- Sending or receiving a HEADERS frame causes the stream to become
- "open". The stream identifier is selected as described in . The same HEADERS frame can also
- cause a stream to immediately become "half closed".
-
-
- Sending a PUSH_PROMISE frame marks the associated stream for
- later use. The stream state for the reserved stream transitions to "reserved
- (local)".
-
-
- Receiving a PUSH_PROMISE frame marks the associated stream as
- reserved by the remote peer. The state of the stream becomes "reserved
- (remote)".
-
-
-
-
- Receiving any frames other than HEADERS or
- PUSH_PROMISE on a stream in this state MUST be treated as a connection error of type
- PROTOCOL_ERROR.
-
-
-
-
-
-
- A stream in the "reserved (local)" state is one that has been promised by sending a
- PUSH_PROMISE frame. A PUSH_PROMISE frame reserves an
- idle stream by associating the stream with an open stream that was initiated by the
- remote peer (see ).
-
-
- In this state, only the following transitions are possible:
-
-
- The endpoint can send a HEADERS frame. This causes the stream to
- open in a "half closed (remote)" state.
-
-
- Either endpoint can send a RST_STREAM frame to cause the stream
- to become "closed". This releases the stream reservation.
-
-
-
-
- An endpoint MUST NOT send any type of frame other than HEADERS or
- RST_STREAM in this state.
-
-
- A PRIORITY frame MAY be received in this state. Receiving any type
- of frame other than RST_STREAM or PRIORITY on a stream
- in this state MUST be treated as a connection
- error of type PROTOCOL_ERROR.
-
-
-
-
-
-
- A stream in the "reserved (remote)" state has been reserved by a remote peer.
-
-
- In this state, only the following transitions are possible:
-
-
- Receiving a HEADERS frame causes the stream to transition to
- "half closed (local)".
-
-
- Either endpoint can send a RST_STREAM frame to cause the stream
- to become "closed". This releases the stream reservation.
-
-
-
-
- An endpoint MAY send a PRIORITY frame in this state to reprioritize
- the reserved stream. An endpoint MUST NOT send any type of frame other than
- RST_STREAM, WINDOW_UPDATE, or PRIORITY
- in this state.
-
-
- Receiving any type of frame other than HEADERS or
- RST_STREAM on a stream in this state MUST be treated as a connection error of type
- PROTOCOL_ERROR.
-
-
-
-
-
-
- A stream in the "open" state may be used by both peers to send frames of any type.
- In this state, sending peers observe advertised stream
- level flow control limits.
-
-
- From this state either endpoint can send a frame with an END_STREAM flag set, which
- causes the stream to transition into one of the "half closed" states: an endpoint
- sending an END_STREAM flag causes the stream state to become "half closed (local)";
- an endpoint receiving an END_STREAM flag causes the stream state to become "half
- closed (remote)".
-
-
- Either endpoint can send a RST_STREAM frame from this state, causing
- it to transition immediately to "closed".
-
-
-
-
-
-
- A stream that is in the "half closed (local)" state cannot be used for sending
- frames. Only WINDOW_UPDATE, PRIORITY and
- RST_STREAM frames can be sent in this state.
-
-
- A stream transitions from this state to "closed" when a frame that contains an
- END_STREAM flag is received, or when either peer sends a RST_STREAM
- frame.
-
-
- A receiver can ignore WINDOW_UPDATE frames in this state, which might
- arrive for a short period after a frame bearing the END_STREAM flag is sent.
-
-
- PRIORITY frames received in this state are used to reprioritize
- streams that depend on the current stream.
-
-
-
-
-
-
- A stream that is "half closed (remote)" is no longer being used by the peer to send
- frames. In this state, an endpoint is no longer obligated to maintain a receiver
- flow control window if it performs flow control.
-
-
- If an endpoint receives additional frames for a stream that is in this state, other
- than WINDOW_UPDATE, PRIORITY or
- RST_STREAM, it MUST respond with a stream error of type
- STREAM_CLOSED.
-
-
- A stream that is "half closed (remote)" can be used by the endpoint to send frames
- of any type. In this state, the endpoint continues to observe advertised stream level flow control limits.
-
-
- A stream can transition from this state to "closed" by sending a frame that contains
- an END_STREAM flag, or when either peer sends a RST_STREAM frame.
-
-
-
-
-
-
- The "closed" state is the terminal state.
-
-
- An endpoint MUST NOT send frames other than PRIORITY on a closed
- stream. An endpoint that receives any frame other than PRIORITY
- after receiving a RST_STREAM MUST treat that as a stream error of type
- STREAM_CLOSED. Similarly, an endpoint that receives any frames after
- receiving a frame with the END_STREAM flag set MUST treat that as a connection error of type
- STREAM_CLOSED, unless the frame is permitted as described below.
-
-
- WINDOW_UPDATE or RST_STREAM frames can be received in
- this state for a short period after a DATA or HEADERS
- frame containing an END_STREAM flag is sent. Until the remote peer receives and
- processes RST_STREAM or the frame bearing the END_STREAM flag, it
- might send frames of these types. Endpoints MUST ignore
- WINDOW_UPDATE or RST_STREAM frames received in this
- state, though endpoints MAY choose to treat frames that arrive a significant time
- after sending END_STREAM as a connection
- error of type PROTOCOL_ERROR.
-
-
- PRIORITY frames can be sent on closed streams to prioritize streams
- that are dependent on the closed stream. Endpoints SHOULD process
- PRIORITY frame, though they can be ignored if the stream has been
- removed from the dependency tree (see ).
-
-
- If this state is reached as a result of sending a RST_STREAM frame,
- the peer that receives the RST_STREAM might have already sent - or
- enqueued for sending - frames on the stream that cannot be withdrawn. An endpoint
- MUST ignore frames that it receives on closed streams after it has sent a
- RST_STREAM frame. An endpoint MAY choose to limit the period over
- which it ignores frames and treat frames that arrive after this time as being in
- error.
-
-
- Flow controlled frames (i.e., DATA) received after sending
- RST_STREAM are counted toward the connection flow control window.
- Even though these frames might be ignored, because they are sent before the sender
- receives the RST_STREAM, the sender will consider the frames to count
- against the flow control window.
-
-
- An endpoint might receive a PUSH_PROMISE frame after it sends
- RST_STREAM. PUSH_PROMISE causes a stream to become
- "reserved" even if the associated stream has been reset. Therefore, a
- RST_STREAM is needed to close an unwanted promised stream.
-
-
-
-
-
- In the absence of more specific guidance elsewhere in this document, implementations
- SHOULD treat the receipt of a frame that is not expressly permitted in the description of
- a state as a connection error of type
- PROTOCOL_ERROR. Frame of unknown types are ignored.
-
-
- An example of the state transitions for an HTTP request/response exchange can be found in
- . An example of the state transitions for server push can be
- found in and .
-
-
-
-
- Streams are identified with an unsigned 31-bit integer. Streams initiated by a client
- MUST use odd-numbered stream identifiers; those initiated by the server MUST use
- even-numbered stream identifiers. A stream identifier of zero (0x0) is used for
- connection control messages; the stream identifier zero cannot be used to establish a
- new stream.
-
-
- HTTP/1.1 requests that are upgraded to HTTP/2 (see ) are
- responded to with a stream identifier of one (0x1). After the upgrade
- completes, stream 0x1 is "half closed (local)" to the client. Therefore, stream 0x1
- cannot be selected as a new stream identifier by a client that upgrades from HTTP/1.1.
-
-
- The identifier of a newly established stream MUST be numerically greater than all
- streams that the initiating endpoint has opened or reserved. This governs streams that
- are opened using a HEADERS frame and streams that are reserved using
- PUSH_PROMISE. An endpoint that receives an unexpected stream identifier
- MUST respond with a connection error of
- type PROTOCOL_ERROR.
-
-
- The first use of a new stream identifier implicitly closes all streams in the "idle"
- state that might have been initiated by that peer with a lower-valued stream identifier.
- For example, if a client sends a HEADERS frame on stream 7 without ever
- sending a frame on stream 5, then stream 5 transitions to the "closed" state when the
- first frame for stream 7 is sent or received.
-
-
- Stream identifiers cannot be reused. Long-lived connections can result in an endpoint
- exhausting the available range of stream identifiers. A client that is unable to
- establish a new stream identifier can establish a new connection for new streams. 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.
-
-
-
-
-
- A peer can limit the number of concurrently active streams using the
- SETTINGS_MAX_CONCURRENT_STREAMS parameter (see ) within a SETTINGS frame. The maximum concurrent
- streams setting is specific to each endpoint and applies only to the peer that receives
- the setting. That is, clients specify the maximum number of concurrent streams the
- server can initiate, and servers specify the maximum number of concurrent streams the
- client can initiate.
-
-
- Streams that are in the "open" state, or either of the "half closed" states count toward
- the maximum number of streams that an endpoint is permitted to open. Streams in any of
- these three states count toward the limit advertised in the
- SETTINGS_MAX_CONCURRENT_STREAMS setting. Streams in either of the
- "reserved" states do not count toward the stream limit.
-
-
- Endpoints MUST NOT exceed the limit set by their peer. An endpoint that receives a
- HEADERS frame that causes their advertised concurrent stream limit to be
- exceeded MUST treat this as a stream error. An
- endpoint that wishes to reduce the value of
- SETTINGS_MAX_CONCURRENT_STREAMS to a value that is below the current
- number of open streams can either close streams that exceed the new value or allow
- streams to complete.
-
-
-
-
-
-
- Using streams for multiplexing introduces contention over use of the TCP connection,
- resulting in blocked streams. A flow control scheme ensures that streams on the same
- connection do not destructively interfere with each other. Flow control is used for both
- individual streams and for the connection as a whole.
-
-
- HTTP/2 provides for flow control through use of the WINDOW_UPDATE frame.
-
-
-
-
- HTTP/2 stream flow control aims to allow a variety of flow control algorithms to be
- used without requiring protocol changes. Flow control in HTTP/2 has the following
- characteristics:
-
-
- Flow control is specific to a connection; i.e., it is "hop-by-hop", not
- "end-to-end".
-
-
- Flow control is based on window update frames. Receivers advertise how many octets
- they are prepared to receive on a stream and for the entire connection. This is a
- credit-based scheme.
-
-
- Flow control is directional with overall control provided by the receiver. A
- receiver MAY choose to set any window size that it desires for each stream and for
- the entire connection. A sender MUST respect flow control limits imposed by a
- receiver. Clients, servers and intermediaries all independently advertise their
- flow control window as a receiver and abide by the flow control limits set by
- their peer when sending.
-
-
- The initial value for the flow control window is 65,535 octets for both new streams
- and the overall connection.
-
-
- The frame type determines whether flow control applies to a frame. Of the frames
- specified in this document, only DATA frames are subject to flow
- control; all other frame types do not consume space in the advertised flow control
- window. This ensures that important control frames are not blocked by flow control.
-
-
- Flow control cannot be disabled.
-
-
- HTTP/2 defines only the format and semantics of the WINDOW_UPDATE
- frame (). This document does not stipulate how a
- receiver decides when to send this frame or the value that it sends, nor does it
- specify how a sender chooses to send packets. Implementations are able to select
- any algorithm that suits their needs.
-
-
-
-
- Implementations are also responsible for managing how requests and responses are sent
- based on priority; choosing how to avoid head of line blocking for requests; and
- managing the creation of new streams. Algorithm choices for these could interact with
- any flow control algorithm.
-
-
-
-
-
- Flow control is defined to protect endpoints that are operating under resource
- constraints. For example, a proxy needs to share memory between many connections, and
- also might have a slow upstream connection and a fast downstream one. Flow control
- addresses cases where the receiver is unable process data on one stream, yet wants to
- continue to process other streams in the same connection.
-
-
- Deployments that do not require this capability can advertise a flow control window of
- the maximum size, incrementing the available space when new data is received. This
- effectively disables flow control for that receiver. Conversely, a sender is always
- subject to the flow control window advertised by the receiver.
-
-
- Deployments with constrained resources (for example, memory) can employ flow control to
- limit the amount of memory a peer can consume. Note, however, that this can lead to
- suboptimal use of available network resources if flow control is enabled without
- knowledge of the bandwidth-delay product (see ).
-
-
- Even with full awareness of the current bandwidth-delay product, implementation of flow
- control can be difficult. When using flow control, the receiver MUST read from the TCP
- receive buffer in a timely fashion. Failure to do so could lead to a deadlock when
- critical frames, such as WINDOW_UPDATE, are not read and acted upon.
-
-
-
-
-
-
- A client can assign a priority for a new stream by including prioritization information in
- the HEADERS frame that opens the stream. For an existing
- stream, the PRIORITY frame can be used to change the
- priority.
-
-
- The purpose of prioritization is to allow an endpoint to express how it would prefer its
- peer allocate resources when managing concurrent streams. Most importantly, priority can
- be used to select streams for transmitting frames when there is limited capacity for
- sending.
-
-
- Streams can be prioritized by marking them as dependent on the completion of other streams
- (). Each dependency is assigned a relative weight, a number
- that is used to determine the relative proportion of available resources that are assigned
- to streams dependent on the same stream.
-
-
-
- Explicitly setting the priority for a stream is input to a prioritization process. It
- does not guarantee any particular processing or transmission order for the stream relative
- to any other stream. An endpoint cannot force a peer to process concurrent streams in a
- particular order using priority. Expressing priority is therefore only ever a suggestion.
-
-
- Providing prioritization information is optional, so default values are used if no
- explicit indicator is provided ().
-
-
-
-
- Each stream can be given an explicit dependency on another stream. Including a
- dependency expresses a preference to allocate resources to the identified stream rather
- than to the dependent stream.
-
-
- A stream that is not dependent on any other stream is given a stream dependency of 0x0.
- In other words, the non-existent stream 0 forms the root of the tree.
-
-
- A stream that depends on another stream is a dependent stream. The stream upon which a
- stream is dependent is a parent stream. A dependency on a stream that is not currently
- in the tree - such as a stream in the "idle" state - results in that stream being given
- a default priority.
-
-
- When assigning a dependency on another stream, the stream is added as a new dependency
- of the parent stream. Dependent streams that share the same parent are not ordered with
- respect to each other. For example, if streams B and C are dependent on stream A, and
- if stream D is created with a dependency on stream A, this results in a dependency order
- of A followed by B, C, and D in any order.
-
-
- /|\
- B C B D C
-]]>
-
-
- An exclusive flag allows for the insertion of a new level of dependencies. The
- exclusive flag causes the stream to become the sole dependency of its parent stream,
- causing other dependencies to become dependent on the exclusive stream. In the
- previous example, if stream D is created with an exclusive dependency on stream A, this
- results in D becoming the dependency parent of B and C.
-
-
- D
- B C / \
- B C
-]]>
-
-
- Inside the dependency tree, a dependent stream SHOULD only be allocated resources if all
- of the streams that it depends on (the chain of parent streams up to 0x0) are either
- closed, or it is not possible to make progress on them.
-
-
- A stream cannot depend on itself. An endpoint MUST treat this as a stream error of type PROTOCOL_ERROR.
-
-
-
-
-
- All dependent streams are allocated an integer weight between 1 and 256 (inclusive).
-
-
- Streams with the same parent SHOULD be allocated resources proportionally based on their
- weight. Thus, if stream B depends on stream A with weight 4, and C depends on stream A
- with weight 12, and if no progress can be made on A, stream B ideally receives one third
- of the resources allocated to stream C.
-
-
-
-
-
- Stream priorities are changed using the PRIORITY frame. Setting a
- dependency causes a stream to become dependent on the identified parent stream.
-
-
- Dependent streams move with their parent stream if the parent is reprioritized. Setting
- a dependency with the exclusive flag for a reprioritized stream moves all the
- dependencies of the new parent stream to become dependent on the reprioritized stream.
-
-
- If a stream is made dependent on one of its own dependencies, the formerly dependent
- stream is first moved to be dependent on the reprioritized stream's previous parent.
- The moved dependency retains its weight.
-
-
-
- For example, consider an original dependency tree where B and C depend on A, D and E
- depend on C, and F depends on D. If A is made dependent on D, then D takes the place
- of A. All other dependency relationships stay the same, except for F, which becomes
- dependent on A if the reprioritization is exclusive.
-
- F B C ==> F A OR A
- / \ | / \ /|\
- D E E B C B C F
- | | |
- F E E
- (intermediate) (non-exclusive) (exclusive)
-]]>
-
-
-
-
-
- When a stream is removed from the dependency tree, its dependencies can be moved to
- become dependent on the parent of the closed stream. The weights of new dependencies
- are recalculated by distributing the weight of the dependency of the closed stream
- proportionally based on the weights of its dependencies.
-
-
- Streams that are removed from the dependency tree cause some prioritization information
- to be lost. Resources are shared between streams with the same parent stream, which
- means that if a stream in that set closes or becomes blocked, any spare capacity
- allocated to a stream is distributed to the immediate neighbors of the stream. However,
- if the common dependency is removed from the tree, those streams share resources with
- streams at the next highest level.
-
-
- For example, assume streams A and B share a parent, and streams C and D both depend on
- stream A. Prior to the removal of stream A, if streams A and D are unable to proceed,
- then stream C receives all the resources dedicated to stream A. If stream A is removed
- from the tree, the weight of stream A is divided between streams C and D. If stream D
- is still unable to proceed, this results in stream C receiving a reduced proportion of
- resources. For equal starting weights, C receives one third, rather than one half, of
- available resources.
-
-
- It is possible for a stream to become closed while prioritization information that
- creates a dependency on that stream is in transit. If a stream identified in a
- dependency has no associated priority information, then the dependent stream is instead
- assigned a default priority. This potentially creates
- suboptimal prioritization, since the stream could be given a priority that is different
- to what is intended.
-
-
- To avoid these problems, an endpoint SHOULD retain stream prioritization state for a
- period after streams become closed. The longer state is retained, the lower the chance
- that streams are assigned incorrect or default priority values.
-
-
- This could create a large state burden for an endpoint, so this state MAY be limited.
- An endpoint MAY apply a fixed upper limit on the number of closed streams for which
- prioritization state is tracked to limit state exposure. The amount of additional state
- an endpoint maintains could be dependent on load; under high load, prioritization state
- can be discarded to limit resource commitments. In extreme cases, an endpoint could
- even discard prioritization state for active or reserved streams. If a fixed limit is
- applied, endpoints SHOULD maintain state for at least as many streams as allowed by
- their setting for SETTINGS_MAX_CONCURRENT_STREAMS.
-
-
- An endpoint receiving a PRIORITY frame that changes the priority of a
- closed stream SHOULD alter the dependencies of the streams that depend on it, if it has
- retained enough state to do so.
-
-
-
-
-
- Providing priority information is optional. Streams are assigned a non-exclusive
- dependency on stream 0x0 by default. Pushed streams
- initially depend on their associated stream. In both cases, streams are assigned a
- default weight of 16.
-
-
-
-
-
-
- HTTP/2 framing permits two classes of error:
-
-
- An error condition that renders the entire connection unusable is a connection error.
-
-
- An error in an individual stream is a stream error.
-
-
-
-
- A list of error codes is included in .
-
-
-
-
- A connection error is any error which prevents further processing of the framing layer,
- or which corrupts any connection state.
-
-
- An endpoint that encounters a connection error SHOULD first send a GOAWAY
- frame () with the stream identifier of the last stream that it
- successfully received from its peer. The GOAWAY frame includes an error
- code that indicates why the connection is terminating. After sending the
- GOAWAY frame, the endpoint MUST close the TCP connection.
-
-
- It is possible that the GOAWAY will not be reliably received by the
- receiving endpoint (see ). In the event of a connection error,
- GOAWAY only provides a best effort attempt to communicate with the peer
- about why the connection is being terminated.
-
-
- An endpoint can end a connection at any time. In particular, an endpoint MAY choose to
- treat a stream error as a connection error. Endpoints SHOULD send a
- GOAWAY frame when ending a connection, providing that circumstances
- permit it.
-
-
-
-
-
- A stream error is an error related to a specific stream that does not affect processing
- of other streams.
-
-
- An endpoint that detects a stream error sends a RST_STREAM frame () that contains the stream identifier of the stream where the error
- occurred. The RST_STREAM frame includes an error code that indicates the
- type of error.
-
-
- A RST_STREAM is the last frame that an endpoint can send on a stream.
- The peer that sends the RST_STREAM frame MUST be prepared to receive any
- frames that were sent or enqueued for sending by the remote peer. These frames can be
- ignored, except where they modify connection state (such as the state maintained for
- header compression, or flow control).
-
-
- Normally, an endpoint SHOULD NOT send more than one RST_STREAM frame for
- any stream. However, an endpoint MAY send additional RST_STREAM frames if
- it receives frames on a closed stream after more than a round-trip time. This behavior
- is permitted to deal with misbehaving implementations.
-
-
- An endpoint MUST NOT send a RST_STREAM in response to an
- RST_STREAM frame, to avoid looping.
-
-
-
-
-
- If the TCP connection is closed or reset while streams remain in open or half closed
- states, then the endpoint MUST assume that those streams were abnormally interrupted and
- could be incomplete.
-
-
-
-
-
-
- HTTP/2 permits extension of the protocol. Protocol extensions can be used to provide
- additional services or alter any aspect of the protocol, within the limitations described
- in this section. Extensions are effective only within the scope of a single HTTP/2
- connection.
-
-
- Extensions are permitted to use new frame types, new
- settings, or new error
- codes. Registries are established for managing these extension points: frame types, settings and
- error codes.
-
-
- Implementations MUST ignore unknown or unsupported values in all extensible protocol
- elements. Implementations MUST discard frames that have unknown or unsupported types.
- This means that any of these extension points can be safely used by extensions without
- prior arrangement or negotiation. However, extension frames that appear in the middle of
- a header block are not permitted; these MUST be treated
- as a connection error of type
- PROTOCOL_ERROR.
-
-
- However, extensions that could change the semantics of existing protocol components MUST
- be negotiated before being used. For example, an extension that changes the layout of the
- HEADERS frame cannot be used until the peer has given a positive signal
- that this is acceptable. In this case, it could also be necessary to coordinate when the
- revised layout comes into effect. Note that treating any frame other than
- DATA frames as flow controlled is such a change in semantics, and can only
- be done through negotiation.
-
-
- This document doesn't mandate a specific method for negotiating the use of an extension,
- but notes that a setting could be used for that
- purpose. If both peers set a value that indicates willingness to use the extension, then
- the extension can be used. If a setting is used for extension negotiation, the initial
- value MUST be defined so that the extension is initially disabled.
-
-
-
-
-
-
- This specification defines a number of frame types, each identified by a unique 8-bit type
- code. Each frame type serves a distinct purpose either in the establishment and management
- of the connection as a whole, or of individual streams.
-
-
- The transmission of specific frame types can alter the state of a connection. If endpoints
- fail to maintain a synchronized view of the connection state, successful communication
- within the connection will no longer be possible. Therefore, it is important that endpoints
- have a shared comprehension of how the state is affected by the use any given frame.
-
-
-
-
- DATA frames (type=0x0) convey arbitrary, variable-length sequences of octets associated
- with a stream. One or more DATA frames are used, for instance, to carry HTTP request or
- response payloads.
-
-
- DATA frames MAY also contain arbitrary padding. Padding can be added to DATA frames to
- obscure the size of messages.
-
-
-
-
-
- The DATA frame contains the following fields:
-
-
- An 8-bit field containing the length of the frame padding in units of octets. This
- field is optional and is only present if the PADDED flag is set.
-
-
- Application data. The amount of data is the remainder of the frame payload after
- subtracting the length of the other fields that are present.
-
-
- Padding octets that contain no application semantic value. Padding octets MUST be set
- to zero when sending and ignored when receiving.
-
-
-
-
-
- The DATA frame defines the following flags:
-
-
- Bit 1 being set indicates that this frame is the last that the endpoint will send for
- the identified stream. Setting this flag causes the stream to enter one of the "half closed" states or the "closed" state.
-
-
- Bit 4 being set indicates that the Pad Length field and any padding that it describes
- is present.
-
-
-
-
- DATA frames MUST be associated with a stream. If a DATA frame is received whose stream
- identifier field is 0x0, the recipient MUST respond with a connection error of type
- PROTOCOL_ERROR.
-
-
- DATA frames are subject to flow control and can only be sent when a stream is in the
- "open" or "half closed (remote)" states. The entire DATA frame payload is included in flow
- control, including Pad Length and Padding fields if present. If a DATA frame is received
- whose stream is not in "open" or "half closed (local)" state, the recipient MUST respond
- with a stream error of type
- STREAM_CLOSED.
-
-
- The total number of padding octets is determined by the value of the Pad Length field. If
- the length of the padding is greater than the length of the frame payload, the recipient
- MUST treat this as a connection error of
- type PROTOCOL_ERROR.
-
-
- A frame can be increased in size by one octet by including a Pad Length field with a
- value of zero.
-
-
-
-
- Padding is a security feature; see .
-
-
-
-
-
- The HEADERS frame (type=0x1) is used to open a stream,
- and additionally carries a header block fragment. HEADERS frames can be sent on a stream
- in the "open" or "half closed (remote)" states.
-
-
-
-
-
- The HEADERS frame payload has the following fields:
-
-
- An 8-bit field containing the length of the frame padding in units of octets. This
- field is only present if the PADDED flag is set.
-
-
- A single bit flag indicates that the stream dependency is exclusive, see . This field is only present if the PRIORITY flag is set.
-
-
- A 31-bit stream identifier for the stream that this stream depends on, see . This field is only present if the PRIORITY flag is set.
-
-
- An 8-bit weight for the stream, see . Add one to the
- value to obtain a weight between 1 and 256. This field is only present if the
- PRIORITY flag is set.
-
-
- A header block fragment.
-
-
- Padding octets that contain no application semantic value. Padding octets MUST be set
- to zero when sending and ignored when receiving.
-
-
-
-
-
- The HEADERS frame defines the following flags:
-
-
-
- Bit 1 being set indicates that the header block is
- the last that the endpoint will send for the identified stream. Setting this flag
- causes the stream to enter one of "half closed"
- states.
-
-
- A HEADERS frame carries the END_STREAM flag that signals the end of a stream.
- However, a HEADERS frame with the END_STREAM flag set can be followed by
- CONTINUATION frames on the same stream. Logically, the
- CONTINUATION frames are part of the HEADERS frame.
-
-
-
-
- Bit 3 being set indicates that this frame contains an entire header block and is not followed by any
- CONTINUATION frames.
-
-
- A HEADERS frame without the END_HEADERS flag set MUST be followed by a
- CONTINUATION frame for the same stream. A receiver MUST treat the
- receipt of any other type of frame or a frame on a different stream as a connection error of type
- PROTOCOL_ERROR.
-
-
-
-
- Bit 4 being set indicates that the Pad Length field and any padding that it
- describes is present.
-
-
-
-
- Bit 6 being set indicates that the Exclusive Flag (E), Stream Dependency, and Weight
- fields are present; see .
-
-
-
-
-
-
- The payload of a HEADERS frame contains a header block
- fragment. A header block that does not fit within a HEADERS frame is continued in
- a CONTINUATION frame.
-
-
-
- HEADERS frames MUST be associated with a stream. If a HEADERS frame is received whose
- stream identifier field is 0x0, the recipient MUST respond with a connection error of type
- PROTOCOL_ERROR.
-
-
-
- The HEADERS frame changes the connection state as described in .
-
-
-
- The HEADERS frame includes optional padding. Padding fields and flags are identical to
- those defined for DATA frames.
-
-
- Prioritization information in a HEADERS frame is logically equivalent to a separate
- PRIORITY frame, but inclusion in HEADERS avoids the potential for churn in
- stream prioritization when new streams are created. Priorization fields in HEADERS frames
- subsequent to the first on a stream reprioritize the
- stream.
-
-
-
-
-
- The PRIORITY frame (type=0x2) specifies the sender-advised
- priority of a stream. It can be sent at any time for an existing stream, including
- closed streams. This enables reprioritization of existing streams.
-
-
-
-
-
- The payload of a PRIORITY frame contains the following fields:
-
-
- A single bit flag indicates that the stream dependency is exclusive, see .
-
-
- A 31-bit stream identifier for the stream that this stream depends on, see .
-
-
- An 8-bit weight for the identified stream dependency, see . Add one to the value to obtain a weight between 1 and 256.
-
-
-
-
-
- The PRIORITY frame does not define any flags.
-
-
-
- The PRIORITY frame is associated with an existing stream. If a PRIORITY frame is received
- with a stream identifier of 0x0, the recipient MUST respond with a connection error of type
- PROTOCOL_ERROR.
-
-
- The PRIORITY frame can be sent on a stream in any of the "reserved (remote)", "open",
- "half closed (local)", "half closed (remote)", or "closed" states, though it cannot be
- sent between consecutive frames that comprise a single header
- block. Note that this frame could arrive after processing or frame sending has
- completed, which would cause it to have no effect on the current stream. For a stream
- that is in the "half closed (remote)" or "closed" - state, this frame can only affect
- processing of the current stream and not frame transmission.
-
-
- The PRIORITY frame is the only frame that can be sent for a stream in the "closed" state.
- This allows for the reprioritization of a group of dependent streams by altering the
- priority of a parent stream, which might be closed. However, a PRIORITY frame sent on a
- closed stream risks being ignored due to the peer having discarded priority state
- information for that stream.
-
-
-
-
-
- The RST_STREAM frame (type=0x3) allows for abnormal termination of a stream. When sent by
- the initiator of a stream, it indicates that they wish to cancel the stream or that an
- error condition has occurred. When sent by the receiver of a stream, it indicates that
- either the receiver is rejecting the stream, requesting that the stream be cancelled, or
- that an error condition has occurred.
-
-
-
-
-
-
- The RST_STREAM frame contains a single unsigned, 32-bit integer identifying the error code. The error code indicates why the stream is being
- terminated.
-
-
-
- The RST_STREAM frame does not define any flags.
-
-
-
- The RST_STREAM frame fully terminates the referenced stream and causes it to enter the
- closed state. After receiving a RST_STREAM on a stream, the receiver MUST NOT send
- additional frames for that stream, with the exception of PRIORITY. However,
- after sending the RST_STREAM, the sending endpoint MUST be prepared to receive and process
- additional frames sent on the stream that might have been sent by the peer prior to the
- arrival of the RST_STREAM.
-
-
-
- RST_STREAM frames MUST be associated with a stream. If a RST_STREAM frame is received
- with a stream identifier of 0x0, the recipient MUST treat this as a connection error of type
- PROTOCOL_ERROR.
-
-
-
- RST_STREAM frames MUST NOT be sent for a stream in the "idle" state. If a RST_STREAM
- frame identifying an idle stream is received, the recipient MUST treat this as a connection error of type
- PROTOCOL_ERROR.
-
-
-
-
-
-
- The SETTINGS frame (type=0x4) conveys configuration parameters that affect how endpoints
- communicate, such as preferences and constraints on peer behavior. The SETTINGS frame is
- also used to acknowledge the receipt of those parameters. Individually, a SETTINGS
- parameter can also be referred to as a "setting".
-
-
- SETTINGS parameters are not negotiated; they describe characteristics of the sending peer,
- which are used by the receiving peer. Different values for the same parameter can be
- advertised by each peer. For example, a client might set a high initial flow control
- window, whereas a server might set a lower value to conserve resources.
-
-
-
- A SETTINGS frame MUST be sent by both endpoints at the start of a connection, and MAY be
- sent at any other time by either endpoint over the lifetime of the connection.
- Implementations MUST support all of the parameters defined by this specification.
-
-
-
- Each parameter in a SETTINGS frame replaces any existing value for that parameter.
- Parameters are processed in the order in which they appear, and a receiver of a SETTINGS
- frame does not need to maintain any state other than the current value of its
- parameters. Therefore, the value of a SETTINGS parameter is the last value that is seen by
- a receiver.
-
-
- SETTINGS parameters are acknowledged by the receiving peer. To enable this, the SETTINGS
- frame defines the following flag:
-
-
- Bit 1 being set indicates that this frame acknowledges receipt and application of the
- peer's SETTINGS frame. When this bit is set, the payload of the SETTINGS frame MUST
- be empty. Receipt of a SETTINGS frame with the ACK flag set and a length field value
- other than 0 MUST be treated as a connection
- error of type FRAME_SIZE_ERROR. For more info, see Settings Synchronization.
-
-
-
-
- SETTINGS frames always apply to a connection, never a single stream. The stream
- identifier for a SETTINGS frame MUST be zero (0x0). If an endpoint receives a SETTINGS
- frame whose stream identifier field is anything other than 0x0, the endpoint MUST respond
- with a connection error of type
- PROTOCOL_ERROR.
-
-
- The SETTINGS frame affects connection state. A badly formed or incomplete SETTINGS frame
- MUST be treated as a connection error of type
- PROTOCOL_ERROR.
-
-
-
-
- The payload of a SETTINGS frame consists of zero or more parameters, each consisting of
- an unsigned 16-bit setting identifier and an unsigned 32-bit value.
-
-
-
-
-
-
-
-
-
- The following parameters are defined:
-
-
-
- Allows the sender to inform the remote endpoint of the maximum size of the header
- compression table used to decode header blocks, in octets. The encoder can select
- any size equal to or less than this value by using signaling specific to the
- header compression format inside a header block. The initial value is 4,096
- octets.
-
-
-
-
- This setting can be use to disable server
- push. An endpoint MUST NOT send a PUSH_PROMISE frame if it
- receives this parameter set to a value of 0. An endpoint that has both set this
- parameter to 0 and had it acknowledged MUST treat the receipt of a
- PUSH_PROMISE frame as a connection error of type
- PROTOCOL_ERROR.
-
-
- The initial value is 1, which indicates that server push is permitted. Any value
- other than 0 or 1 MUST be treated as a connection error of type
- PROTOCOL_ERROR.
-
-
-
-
- Indicates the maximum number of concurrent streams that the sender will allow.
- This limit is directional: it applies to the number of streams that the sender
- permits the receiver to create. Initially there is no limit to this value. It is
- recommended that this value be no smaller than 100, so as to not unnecessarily
- limit parallelism.
-
-
- A value of 0 for SETTINGS_MAX_CONCURRENT_STREAMS SHOULD NOT be treated as special
- by endpoints. A zero value does prevent the creation of new streams, however this
- can also happen for any limit that is exhausted with active streams. Servers
- SHOULD only set a zero value for short durations; if a server does not wish to
- accept requests, closing the connection could be preferable.
-
-
-
-
- Indicates the sender's initial window size (in octets) for stream level flow
- control. The initial value is 216-1 (65,535) octets.
-
-
- This setting affects the window size of all streams, including existing streams,
- see .
-
-
- Values above the maximum flow control window size of 231-1 MUST
- be treated as a connection error of
- type FLOW_CONTROL_ERROR.
-
-
-
-
- Indicates the size of the largest frame payload that the sender is willing to
- receive, in octets.
-
-
- The initial value is 214 (16,384) octets. The value advertised by
- an endpoint MUST be between this initial value and the maximum allowed frame size
- (224-1 or 16,777,215 octets), inclusive. Values outside this range
- MUST be treated as a connection error
- of type PROTOCOL_ERROR.
-
-
-
-
- This advisory setting informs a peer of the maximum size of header list that the
- sender is prepared to accept, in octets. The value is based on the uncompressed
- size of header fields, including the length of the name and value in octets plus
- an overhead of 32 octets for each header field.
-
-
- For any given request, a lower limit than what is advertised MAY be enforced. The
- initial value of this setting is unlimited.
-
-
-
-
-
- An endpoint that receives a SETTINGS frame with any unknown or unsupported identifier
- MUST ignore that setting.
-
-
-
-
-
- Most values in SETTINGS benefit from or require an understanding of when the peer has
- received and applied the changed parameter values. In order to provide
- such synchronization timepoints, the recipient of a SETTINGS frame in which the ACK flag
- is not set MUST apply the updated parameters as soon as possible upon receipt.
-
-
- The values in the SETTINGS frame MUST be processed in the order they appear, with no
- other frame processing between values. Unsupported parameters MUST be ignored. Once
- all values have been processed, the recipient MUST immediately emit a SETTINGS frame
- with the ACK flag set. Upon receiving a SETTINGS frame with the ACK flag set, the sender
- of the altered parameters can rely on the setting having been applied.
-
-
- If the sender of a SETTINGS frame does not receive an acknowledgement within a
- reasonable amount of time, it MAY issue a connection error of type
- SETTINGS_TIMEOUT.
-
-
-
-
-
-
- The PUSH_PROMISE frame (type=0x5) is used to notify the peer endpoint in advance of
- streams the sender intends to initiate. The PUSH_PROMISE frame includes the unsigned
- 31-bit identifier of the stream the endpoint plans to create along with a set of headers
- that provide additional context for the stream. contains a
- thorough description of the use of PUSH_PROMISE frames.
-
-
-
-
-
-
- The PUSH_PROMISE frame payload has the following fields:
-
-
- An 8-bit field containing the length of the frame padding in units of octets. This
- field is only present if the PADDED flag is set.
-
-
- A single reserved bit.
-
-
- An unsigned 31-bit integer that identifies the stream that is reserved by the
- PUSH_PROMISE. The promised stream identifier MUST be a valid choice for the next
- stream sent by the sender (see new stream
- identifier).
-
-
- A header block fragment containing request header
- fields.
-
-
- Padding octets.
-
-
-
-
-
- The PUSH_PROMISE frame defines the following flags:
-
-
-
- Bit 3 being set indicates that this frame contains an entire header block and is not followed by any
- CONTINUATION frames.
-
-
- A PUSH_PROMISE frame without the END_HEADERS flag set MUST be followed by a
- CONTINUATION frame for the same stream. A receiver MUST treat the receipt of any
- other type of frame or a frame on a different stream as a connection error of type
- PROTOCOL_ERROR.
-
-
-
-
- Bit 4 being set indicates that the Pad Length field and any padding that it
- describes is present.
-
-
-
-
-
-
- PUSH_PROMISE frames MUST be associated with an existing, peer-initiated stream. The stream
- identifier of a PUSH_PROMISE frame indicates the stream it is associated with. If the
- stream identifier field specifies the value 0x0, a recipient MUST respond with a connection error of type
- PROTOCOL_ERROR.
-
-
-
- Promised streams are not required to be used in the order they are promised. The
- PUSH_PROMISE only reserves stream identifiers for later use.
-
-
-
- PUSH_PROMISE MUST NOT be sent if the SETTINGS_ENABLE_PUSH setting of the
- peer endpoint is set to 0. An endpoint that has set this setting and has received
- acknowledgement MUST treat the receipt of a PUSH_PROMISE frame as a connection error of type
- PROTOCOL_ERROR.
-
-
- Recipients of PUSH_PROMISE frames can choose to reject promised streams by returning a
- RST_STREAM referencing the promised stream identifier back to the sender of
- the PUSH_PROMISE.
-
-
-
- A PUSH_PROMISE frame modifies the connection state in two ways. The inclusion of a header block potentially modifies the state maintained for
- header compression. PUSH_PROMISE also reserves a stream for later use, causing the
- promised stream to enter the "reserved" state. A sender MUST NOT send a PUSH_PROMISE on a
- stream unless that stream is either "open" or "half closed (remote)"; the sender MUST
- ensure that the promised stream is a valid choice for a new stream identifier (that is, the promised stream MUST
- be in the "idle" state).
-
-
- Since PUSH_PROMISE reserves a stream, ignoring a PUSH_PROMISE frame causes the stream
- state to become indeterminate. A receiver MUST treat the receipt of a PUSH_PROMISE on a
- stream that is neither "open" nor "half closed (local)" as a connection error of type
- PROTOCOL_ERROR. However, an endpoint that has sent
- RST_STREAM on the associated stream MUST handle PUSH_PROMISE frames that
- might have been created before the RST_STREAM frame is received and
- processed.
-
-
- A receiver MUST treat the receipt of a PUSH_PROMISE that promises an illegal stream identifier (that is, an identifier for a
- stream that is not currently in the "idle" state) as a connection error of type
- PROTOCOL_ERROR.
-
-
-
- The PUSH_PROMISE frame includes optional padding. Padding fields and flags are identical
- to those defined for DATA frames.
-
-
-
-
-
- The PING frame (type=0x6) is a mechanism for measuring a minimal round trip time from the
- sender, as well as determining whether an idle connection is still functional. PING
- frames can be sent from any endpoint.
-
-
-
-
-
-
- In addition to the frame header, PING frames MUST contain 8 octets of data in the payload.
- A sender can include any value it chooses and use those bytes in any fashion.
-
-
- Receivers of a PING frame that does not include an ACK flag MUST send a PING frame with
- the ACK flag set in response, with an identical payload. PING responses SHOULD be given
- higher priority than any other frame.
-
-
-
- The PING frame defines the following flags:
-
-
- Bit 1 being set indicates that this PING frame is a PING response. An endpoint MUST
- set this flag in PING responses. An endpoint MUST NOT respond to PING frames
- containing this flag.
-
-
-
-
- PING frames are not associated with any individual stream. If a PING frame is received
- with a stream identifier field value other than 0x0, the recipient MUST respond with a
- connection error of type
- PROTOCOL_ERROR.
-
-
- Receipt of a PING frame with a length field value other than 8 MUST be treated as a connection error of type
- FRAME_SIZE_ERROR.
-
-
-
-
-
-
- The GOAWAY frame (type=0x7) informs the remote peer to stop creating streams on this
- connection. GOAWAY can be sent by either the client or the server. Once sent, the sender
- will ignore frames sent on any new streams with identifiers higher than the included last
- stream identifier. Receivers of a GOAWAY frame MUST NOT open additional streams on the
- connection, although a new connection can be established for new streams.
-
-
- The purpose of this frame is to allow an endpoint to gracefully stop accepting new
- streams, while still finishing processing of previously established streams. This enables
- administrative actions, like server maintainance.
-
-
- There is an inherent race condition between an endpoint starting new streams and the
- remote sending a GOAWAY frame. To deal with this case, the GOAWAY contains the stream
- identifier of the last peer-initiated stream which was or might be processed on the
- sending endpoint in this connection. For instance, if the server sends a GOAWAY frame,
- the identified stream is the highest numbered stream initiated by the client.
-
-
- If the receiver of the GOAWAY has sent data on streams with a higher stream identifier
- than what is indicated in the GOAWAY frame, those streams are not or will not be
- processed. The receiver of the GOAWAY frame can treat the streams as though they had
- never been created at all, thereby allowing those streams to be retried later on a new
- connection.
-
-
- Endpoints SHOULD always send a GOAWAY frame before closing a connection so that the remote
- can know whether a stream has been partially processed or not. For example, if an HTTP
- client sends a POST at the same time that a server closes a connection, the client cannot
- know if the server started to process that POST request if the server does not send a
- GOAWAY frame to indicate what streams it might have acted on.
-
-
- An endpoint might choose to close a connection without sending GOAWAY for misbehaving
- peers.
-
-
-
-
-
-
- The GOAWAY frame does not define any flags.
-
-
- The GOAWAY frame applies to the connection, not a specific stream. An endpoint MUST treat
- a GOAWAY frame with a stream identifier other than 0x0 as a connection error of type
- PROTOCOL_ERROR.
-
-
- The last stream identifier in the GOAWAY frame contains the highest numbered stream
- identifier for which the sender of the GOAWAY frame might have taken some action on, or
- might yet take action on. All streams up to and including the identified stream might
- have been processed in some way. The last stream identifier can be set to 0 if no streams
- were processed.
-
-
- In this context, "processed" means that some data from the stream was passed to some
- higher layer of software that might have taken some action as a result.
-
-
- If a connection terminates without a GOAWAY frame, the last stream identifier is
- effectively the highest possible stream identifier.
-
-
- On streams with lower or equal numbered identifiers that were not closed completely prior
- to the connection being closed, re-attempting requests, transactions, or any protocol
- activity is not possible, with the exception of idempotent actions like HTTP GET, PUT, or
- DELETE. Any protocol activity that uses higher numbered streams can be safely retried
- using a new connection.
-
-
- Activity on streams numbered lower or equal to the last stream identifier might still
- complete successfully. The sender of a GOAWAY frame might gracefully shut down a
- connection by sending a GOAWAY frame, maintaining the connection in an open state until
- all in-progress streams complete.
-
-
- An endpoint MAY send multiple GOAWAY frames if circumstances change. For instance, an
- endpoint that sends GOAWAY with NO_ERROR during graceful shutdown could
- subsequently encounter an condition that requires immediate termination of the connection.
- The last stream identifier from the last GOAWAY frame received indicates which streams
- could have been acted upon. Endpoints MUST NOT increase the value they send in the last
- stream identifier, since the peers might already have retried unprocessed requests on
- another connection.
-
-
- A client that is unable to retry requests loses all requests that are in flight when the
- server closes the connection. This is especially true for intermediaries that might
- not be serving clients using HTTP/2. A server that is attempting to gracefully shut down
- a connection SHOULD send an initial GOAWAY frame with the last stream identifier set to
- 231-1 and a NO_ERROR code. This signals to the client that
- a shutdown is imminent and that no further requests can be initiated. After waiting at
- least one round trip time, the server can send another GOAWAY frame with an updated last
- stream identifier. This ensures that a connection can be cleanly shut down without losing
- requests.
-
-
-
- After sending a GOAWAY frame, the sender can discard frames for streams with identifiers
- higher than the identified last stream. However, any frames that alter connection state
- cannot be completely ignored. For instance, HEADERS,
- PUSH_PROMISE and CONTINUATION frames MUST be minimally
- processed to ensure the state maintained for header compression is consistent (see ); similarly DATA frames MUST be counted toward the connection flow
- control window. Failure to process these frames can cause flow control or header
- compression state to become unsynchronized.
-
-
-
- The GOAWAY frame also contains a 32-bit error code that
- contains the reason for closing the connection.
-
-
- Endpoints MAY append opaque data to the payload of any GOAWAY frame. Additional debug
- data is intended for diagnostic purposes only and carries no semantic value. Debug
- information could contain security- or privacy-sensitive data. Logged or otherwise
- persistently stored debug data MUST have adequate safeguards to prevent unauthorized
- access.
-
-
-
-
-
- The WINDOW_UPDATE frame (type=0x8) is used to implement flow control; see for an overview.
-
-
- Flow control operates at two levels: on each individual stream and on the entire
- connection.
-
-
- Both types of flow control are hop-by-hop; that is, only between the two endpoints.
- Intermediaries do not forward WINDOW_UPDATE frames between dependent connections.
- However, throttling of data transfer by any receiver can indirectly cause the propagation
- of flow control information toward the original sender.
-
-
- Flow control only applies to frames that are identified as being subject to flow control.
- Of the frame types defined in this document, this includes only DATA frames.
- Frames that are exempt from flow control MUST be accepted and processed, unless the
- receiver is unable to assign resources to handling the frame. A receiver MAY respond with
- a stream error or connection error of type
- FLOW_CONTROL_ERROR if it is unable to accept a frame.
-
-
-
-
-
- The payload of a WINDOW_UPDATE frame is one reserved bit, plus an unsigned 31-bit integer
- indicating the number of octets that the sender can transmit in addition to the existing
- flow control window. The legal range for the increment to the flow control window is 1 to
- 231-1 (0x7fffffff) octets.
-
-
- The WINDOW_UPDATE frame does not define any flags.
-
-
- The WINDOW_UPDATE frame can be specific to a stream or to the entire connection. In the
- former case, the frame's stream identifier indicates the affected stream; in the latter,
- the value "0" indicates that the entire connection is the subject of the frame.
-
-
- A receiver MUST treat the receipt of a WINDOW_UPDATE frame with an flow control window
- increment of 0 as a stream error of type
- PROTOCOL_ERROR; errors on the connection flow control window MUST be
- treated as a connection error.
-
-
- WINDOW_UPDATE can be sent by a peer that has sent a frame bearing the END_STREAM flag.
- This means that a receiver could receive a WINDOW_UPDATE frame on a "half closed (remote)"
- or "closed" stream. A receiver MUST NOT treat this as an error, see .
-
-
- A receiver that receives a flow controlled frame MUST always account for its contribution
- against the connection flow control window, unless the receiver treats this as a connection error. This is necessary even if the
- frame is in error. Since the sender counts the frame toward the flow control window, if
- the receiver does not, the flow control window at sender and receiver can become
- different.
-
-
-
-
- Flow control in HTTP/2 is implemented using a window kept by each sender on every
- stream. The flow control window is a simple integer value that indicates how many octets
- of data the sender is permitted to transmit; as such, its size is a measure of the
- buffering capacity of the receiver.
-
-
- Two flow control windows are applicable: the stream flow control window and the
- connection flow control window. The sender MUST NOT send a flow controlled frame with a
- length that exceeds the space available in either of the flow control windows advertised
- by the receiver. Frames with zero length with the END_STREAM flag set (that is, an
- empty DATA frame) MAY be sent if there is no available space in either
- flow control window.
-
-
- For flow control calculations, the 9 octet frame header is not counted.
-
-
- After sending a flow controlled frame, the sender reduces the space available in both
- windows by the length of the transmitted frame.
-
-
- The receiver of a frame sends a WINDOW_UPDATE frame as it consumes data and frees up
- space in flow control windows. Separate WINDOW_UPDATE frames are sent for the stream
- and connection level flow control windows.
-
-
- A sender that receives a WINDOW_UPDATE frame updates the corresponding window by the
- amount specified in the frame.
-
-
- A sender MUST NOT allow a flow control window to exceed 231-1 octets.
- If a sender receives a WINDOW_UPDATE that causes a flow control window to exceed this
- maximum it MUST terminate either the stream or the connection, as appropriate. For
- streams, the sender sends a RST_STREAM with the error code of
- FLOW_CONTROL_ERROR code; for the connection, a GOAWAY
- frame with a FLOW_CONTROL_ERROR code.
-
-
- Flow controlled frames from the sender and WINDOW_UPDATE frames from the receiver are
- completely asynchronous with respect to each other. This property allows a receiver to
- aggressively update the window size kept by the sender to prevent streams from stalling.
-
-
-
-
-
- When an HTTP/2 connection is first established, new streams are created with an initial
- flow control window size of 65,535 octets. The connection flow control window is 65,535
- octets. Both endpoints can adjust the initial window size for new streams by including
- a value for SETTINGS_INITIAL_WINDOW_SIZE in the SETTINGS
- frame that forms part of the connection preface. The connection flow control window can
- only be changed using WINDOW_UPDATE frames.
-
-
- Prior to receiving a SETTINGS frame that sets a value for
- SETTINGS_INITIAL_WINDOW_SIZE, an endpoint can only use the default
- initial window size when sending flow controlled frames. Similarly, the connection flow
- control window is set to the default initial window size until a WINDOW_UPDATE frame is
- received.
-
-
- A SETTINGS frame can alter the initial flow control window size for all
- current streams. When the value of SETTINGS_INITIAL_WINDOW_SIZE changes,
- a receiver MUST adjust the size of all stream flow control windows that it maintains by
- the difference between the new value and the old value.
-
-
- A change to SETTINGS_INITIAL_WINDOW_SIZE can cause the available space in
- a flow control window to become negative. A sender MUST track the negative flow control
- window, and MUST NOT send new flow controlled frames until it receives WINDOW_UPDATE
- frames that cause the flow control window to become positive.
-
-
- For example, if the client sends 60KB immediately on connection establishment, and the
- server sets the initial window size to be 16KB, the client will recalculate the
- available flow control window to be -44KB on receipt of the SETTINGS
- frame. The client retains a negative flow control window until WINDOW_UPDATE frames
- restore the window to being positive, after which the client can resume sending.
-
-
- A SETTINGS frame cannot alter the connection flow control window.
-
-
- An endpoint MUST treat a change to SETTINGS_INITIAL_WINDOW_SIZE that
- causes any flow control window to exceed the maximum size as a connection error of type
- FLOW_CONTROL_ERROR.
-
-
-
-
-
- A receiver that wishes to use a smaller flow control window than the current size can
- send a new SETTINGS frame. However, the receiver MUST be prepared to
- receive data that exceeds this window size, since the sender might send data that
- exceeds the lower limit prior to processing the SETTINGS frame.
-
-
- After sending a SETTINGS frame that reduces the initial flow control window size, a
- receiver has two options for handling streams that exceed flow control limits:
-
-
- The receiver can immediately send RST_STREAM with
- FLOW_CONTROL_ERROR error code for the affected streams.
-
-
- The receiver can accept the streams and tolerate the resulting head of line
- blocking, sending WINDOW_UPDATE frames as it consumes data.
-
-
-
-
-
-
-
-
- The CONTINUATION frame (type=0x9) is used to continue a sequence of header block fragments. Any number of CONTINUATION frames can
- be sent on an existing stream, as long as the preceding frame is on the same stream and is
- a HEADERS, PUSH_PROMISE or CONTINUATION frame without the
- END_HEADERS flag set.
-
-
-
-
-
-
- The CONTINUATION frame payload contains a header block
- fragment.
-
-
-
- The CONTINUATION frame defines the following flag:
-
-
-
- Bit 3 being set indicates that this frame ends a header
- block.
-
-
- If the END_HEADERS bit is not set, this frame MUST be followed by another
- CONTINUATION frame. A receiver MUST treat the receipt of any other type of frame or
- a frame on a different stream as a connection
- error of type PROTOCOL_ERROR.
-
-
-
-
-
-
- The CONTINUATION frame changes the connection state as defined in .
-
-
-
- CONTINUATION frames MUST be associated with a stream. If a CONTINUATION frame is received
- whose stream identifier field is 0x0, the recipient MUST respond with a connection error of type PROTOCOL_ERROR.
-
-
-
- A CONTINUATION frame MUST be preceded by a HEADERS,
- PUSH_PROMISE or CONTINUATION frame without the END_HEADERS flag set. A
- recipient that observes violation of this rule MUST respond with a connection error of type
- PROTOCOL_ERROR.
-
-
-
-
-
-
- Error codes are 32-bit fields that are used in RST_STREAM and
- GOAWAY frames to convey the reasons for the stream or connection error.
-
-
-
- Error codes share a common code space. Some error codes apply only to either streams or the
- entire connection and have no defined semantics in the other context.
-
-
-
- The following error codes are defined:
-
-
- The associated condition is not as a result of an error. For example, a
- GOAWAY might include this code to indicate graceful shutdown of a
- connection.
-
-
- The endpoint detected an unspecific protocol error. This error is for use when a more
- specific error code is not available.
-
-
- The endpoint encountered an unexpected internal error.
-
-
- The endpoint detected that its peer violated the flow control protocol.
-
-
- The endpoint sent a SETTINGS frame, but did not receive a response in a
- timely manner. See Settings Synchronization.
-
-
- The endpoint received a frame after a stream was half closed.
-
-
- The endpoint received a frame with an invalid size.
-
-
- The endpoint refuses the stream prior to performing any application processing, see
- for details.
-
-
- Used by the endpoint to indicate that the stream is no longer needed.
-
-
- The endpoint is unable to maintain the header compression context for the connection.
-
-
- The connection established in response to a CONNECT
- request was reset or abnormally closed.
-
-
- The endpoint detected that its peer is exhibiting a behavior that might be generating
- excessive load.
-
-
- The underlying transport has properties that do not meet minimum security
- requirements (see ).
-
-
-
-
- Unknown or unsupported error codes MUST NOT trigger any special behavior. These MAY be
- treated by an implementation as being equivalent to INTERNAL_ERROR.
-
-
-
-
-
- HTTP/2 is intended to be as compatible as possible with current uses of HTTP. This means
- that, from the application perspective, the features of the protocol are largely
- unchanged. To achieve this, all request and response semantics are preserved, although the
- syntax of conveying those semantics has changed.
-
-
- Thus, the specification and requirements of HTTP/1.1 Semantics and Content , Conditional Requests , Range Requests , Caching and Authentication are applicable to HTTP/2. Selected portions of HTTP/1.1 Message Syntax
- and Routing , such as the HTTP and HTTPS URI schemes, are also
- applicable in HTTP/2, but the expression of those semantics for this protocol are defined
- in the sections below.
-
-
-
-
- A client sends an HTTP request on a new stream, using a previously unused stream identifier. A server sends an HTTP response on
- the same stream as the request.
-
-
- An HTTP message (request or response) consists of:
-
-
- for a response only, zero or more HEADERS frames (each followed by zero
- or more CONTINUATION frames) containing the message headers of
- informational (1xx) HTTP responses (see and ),
- and
-
-
- one HEADERS frame (followed by zero or more CONTINUATION
- frames) containing the message headers (see ), and
-
-
- zero or more DATA frames containing the message payload (see ), and
-
-
- optionally, one HEADERS frame, followed by zero or more
- CONTINUATION frames containing the trailer-part, if present (see ).
-
-
- The last frame in the sequence bears an END_STREAM flag, noting that a
- HEADERS frame bearing the END_STREAM flag can be followed by
- CONTINUATION frames that carry any remaining portions of the header block.
-
-
- Other frames (from any stream) MUST NOT occur between either HEADERS frame
- and any CONTINUATION frames that might follow.
-
-
-
- Trailing header fields are carried in a header block that also terminates the stream.
- That is, a sequence starting with a HEADERS frame, followed by zero or more
- CONTINUATION frames, where the HEADERS frame bears an
- END_STREAM flag. Header blocks after the first that do not terminate the stream are not
- part of an HTTP request or response.
-
-
- A HEADERS frame (and associated CONTINUATION frames) can
- only appear at the start or end of a stream. An endpoint that receives a
- HEADERS frame without the END_STREAM flag set after receiving a final
- (non-informational) status code MUST treat the corresponding request or response as malformed.
-
-
-
- An HTTP request/response exchange fully consumes a single stream. A request starts with
- the HEADERS frame that puts the stream into an "open" state. The request
- ends with a frame bearing END_STREAM, which causes the stream to become "half closed
- (local)" for the client and "half closed (remote)" for the server. A response starts with
- a HEADERS frame and ends with a frame bearing END_STREAM, which places the
- stream in the "closed" state.
-
-
-
-
-
- HTTP/2 removes support for the 101 (Switching Protocols) informational status code
- ().
-
-
- The semantics of 101 (Switching Protocols) aren't applicable to a multiplexed protocol.
- Alternative protocols are able to use the same mechanisms that HTTP/2 uses to negotiate
- their use (see ).
-
-
-
-
-
- HTTP header fields carry information as a series of key-value pairs. For a listing of
- registered HTTP headers, see the Message Header Field Registry maintained at .
-
-
-
-
- While HTTP/1.x used the message start-line (see ) to convey the target URI and method of the request, and the
- status code for the response, HTTP/2 uses special pseudo-header fields beginning with
- ':' character (ASCII 0x3a) for this purpose.
-
-
- Pseudo-header fields are not HTTP header fields. Endpoints MUST NOT generate
- pseudo-header fields other than those defined in this document.
-
-
- Pseudo-header fields are only valid in the context in which they are defined.
- Pseudo-header fields defined for requests MUST NOT appear in responses; pseudo-header
- fields defined for responses MUST NOT appear in requests. Pseudo-header fields MUST
- NOT appear in trailers. Endpoints MUST treat a request or response that contains
- undefined or invalid pseudo-header fields as malformed.
-
-
- Just as in HTTP/1.x, header field names are strings of ASCII characters that are
- compared in a case-insensitive fashion. However, header field names MUST be converted
- to lowercase prior to their encoding in HTTP/2. A request or response containing
- uppercase header field names MUST be treated as malformed.
-
-
- All pseudo-header fields MUST appear in the header block before regular header fields.
- Any request or response that contains a pseudo-header field that appears in a header
- block after a regular header field MUST be treated as malformed.
-
-
-
-
-
- HTTP/2 does not use the Connection header field to
- indicate connection-specific header fields; in this protocol, connection-specific
- metadata is conveyed by other means. An endpoint MUST NOT generate a HTTP/2 message
- containing connection-specific header fields; any message containing
- connection-specific header fields MUST be treated as malformed.
-
-
- This means that an intermediary transforming an HTTP/1.x message to HTTP/2 will need
- to remove any header fields nominated by the Connection header field, along with the
- Connection header field itself. Such intermediaries SHOULD also remove other
- connection-specific header fields, such as Keep-Alive, Proxy-Connection,
- Transfer-Encoding and Upgrade, even if they are not nominated by Connection.
-
-
- One exception to this is the TE header field, which MAY be present in an HTTP/2
- request, but when it is MUST NOT contain any value other than "trailers".
-
-
-
-
- HTTP/2 purposefully does not support upgrade to another protocol. The handshake
- methods described in are believed sufficient to
- negotiate the use of alternative protocols.
-
-
-
-
-
-
-
- The following pseudo-header fields are defined for HTTP/2 requests:
-
-
-
- The :method pseudo-header field includes the HTTP
- method ().
-
-
-
-
- The :scheme pseudo-header field includes the scheme
- portion of the target URI ().
-
-
- :scheme is not restricted to http and https schemed URIs. A
- proxy or gateway can translate requests for non-HTTP schemes, enabling the use
- of HTTP to interact with non-HTTP services.
-
-
-
-
- The :authority pseudo-header field includes the
- authority portion of the target URI (). The authority MUST NOT include the deprecated userinfo subcomponent for http
- or https schemed URIs.
-
-
- To ensure that the HTTP/1.1 request line can be reproduced accurately, this
- pseudo-header field MUST be omitted when translating from an HTTP/1.1 request
- that has a request target in origin or asterisk form (see ). Clients that generate
- HTTP/2 requests directly SHOULD use the :authority pseudo-header
- field instead of the Host header field. An
- intermediary that converts an HTTP/2 request to HTTP/1.1 MUST create a Host header field if one is not present in a request by
- copying the value of the :authority pseudo-header
- field.
-
-
-
-
- The :path pseudo-header field includes the path and
- query parts of the target URI (the path-absolute
- production from and optionally a '?' character
- followed by the query production, see and ). A request in asterisk form includes the value '*' for the
- :path pseudo-header field.
-
-
- This pseudo-header field MUST NOT be empty for http
- or https URIs; http or
- https URIs that do not contain a path component
- MUST include a value of '/'. The exception to this rule is an OPTIONS request
- for an http or https
- URI that does not include a path component; these MUST include a :path pseudo-header field with a value of '*' (see ).
-
-
-
-
-
- All HTTP/2 requests MUST include exactly one valid value for the :method, :scheme, and :path pseudo-header fields, unless it is a CONNECT request. An HTTP request that omits mandatory
- pseudo-header fields is malformed.
-
-
- HTTP/2 does not define a way to carry the version identifier that is included in the
- HTTP/1.1 request line.
-
-
-
-
-
- For HTTP/2 responses, a single :status pseudo-header
- field is defined that carries the HTTP status code field (see ). This pseudo-header field MUST be included in all
- responses, otherwise the response is malformed.
-
-
- HTTP/2 does not define a way to carry the version or reason phrase that is included in
- an HTTP/1.1 status line.
-
-
-
-
-
- The Cookie header field can carry a significant amount of
- redundant data.
-
-
- The Cookie header field uses a semi-colon (";") to delimit cookie-pairs (or "crumbs").
- This header field doesn't follow the list construction rules in HTTP (see ), which prevents cookie-pairs from
- being separated into different name-value pairs. This can significantly reduce
- compression efficiency as individual cookie-pairs are updated.
-
-
- To allow for better compression efficiency, the Cookie header field MAY be split into
- separate header fields, each with one or more cookie-pairs. If there are multiple
- Cookie header fields after decompression, these MUST be concatenated into a single
- octet string using the two octet delimiter of 0x3B, 0x20 (the ASCII string "; ")
- before being passed into a non-HTTP/2 context, such as an HTTP/1.1 connection, or a
- generic HTTP server application.
-
-
-
- Therefore, the following two lists of Cookie header fields are semantically
- equivalent.
-
-
-
-
-
-
-
- A malformed request or response is one that is an otherwise valid sequence of HTTP/2
- frames, but is otherwise invalid due to the presence of extraneous frames, prohibited
- header fields, the absence of mandatory header fields, or the inclusion of uppercase
- header field names.
-
-
- A request or response that includes an entity body can include a content-length header field. A request or response is also
- malformed if the value of a content-length header field
- does not equal the sum of the DATA frame payload lengths that form the
- body. A response that is defined to have no payload, as described in , can have a non-zero
- content-length header field, even though no content is
- included in DATA frames.
-
-
- Intermediaries that process HTTP requests or responses (i.e., any intermediary not
- acting as a tunnel) MUST NOT forward a malformed request or response. Malformed
- requests or responses that are detected MUST be treated as a stream error of type PROTOCOL_ERROR.
-
-
- For malformed requests, a server MAY send an HTTP response prior to closing or
- resetting the stream. Clients MUST NOT accept a malformed response. Note that these
- requirements are intended to protect against several types of common attacks against
- HTTP; they are deliberately strict, because being permissive can expose
- implementations to these vulnerabilities.
-
-
-
-
-
-
- This section shows HTTP/1.1 requests and responses, with illustrations of equivalent
- HTTP/2 requests and responses.
-
-
- An HTTP GET request includes request header fields and no body and is therefore
- transmitted as a single HEADERS frame, followed by zero or more
- CONTINUATION frames containing the serialized block of request header
- fields. The HEADERS frame in the following has both the END_HEADERS and
- END_STREAM flags set; no CONTINUATION frames are sent:
-
-
-
- + END_STREAM
- Accept: image/jpeg + END_HEADERS
- :method = GET
- :scheme = https
- :path = /resource
- host = example.org
- accept = image/jpeg
-]]>
-
-
-
- Similarly, a response that includes only response header fields is transmitted as a
- HEADERS frame (again, followed by zero or more
- CONTINUATION frames) containing the serialized block of response header
- fields.
-
-
-
- + END_STREAM
- Expires: Thu, 23 Jan ... + END_HEADERS
- :status = 304
- etag = "xyzzy"
- expires = Thu, 23 Jan ...
-]]>
-
-
-
- An HTTP POST request that includes request header fields and payload data is transmitted
- as one HEADERS frame, followed by zero or more
- CONTINUATION frames containing the request header fields, followed by one
- or more DATA frames, with the last CONTINUATION (or
- HEADERS) frame having the END_HEADERS flag set and the final
- DATA frame having the END_STREAM flag set:
-
-
-
- - END_STREAM
- Content-Type: image/jpeg - END_HEADERS
- Content-Length: 123 :method = POST
- :path = /resource
- {binary data} :scheme = https
-
- CONTINUATION
- + END_HEADERS
- content-type = image/jpeg
- host = example.org
- content-length = 123
-
- DATA
- + END_STREAM
- {binary data}
-]]>
-
- Note that data contributing to any given header field could be spread between header
- block fragments. The allocation of header fields to frames in this example is
- illustrative only.
-
-
-
-
- A response that includes header fields and payload data is transmitted as a
- HEADERS frame, followed by zero or more CONTINUATION
- frames, followed by one or more DATA frames, with the last
- DATA frame in the sequence having the END_STREAM flag set:
-
-
-
- - END_STREAM
- Content-Length: 123 + END_HEADERS
- :status = 200
- {binary data} content-type = image/jpeg
- content-length = 123
-
- DATA
- + END_STREAM
- {binary data}
-]]>
-
-
-
- Trailing header fields are sent as a header block after both the request or response
- header block and all the DATA frames have been sent. The
- HEADERS frame starting the trailers header block has the END_STREAM flag
- set.
-
-
-
- - END_STREAM
- Transfer-Encoding: chunked + END_HEADERS
- Trailer: Foo :status = 200
- content-length = 123
- 123 content-type = image/jpeg
- {binary data} trailer = Foo
- 0
- Foo: bar DATA
- - END_STREAM
- {binary data}
-
- HEADERS
- + END_STREAM
- + END_HEADERS
- foo = bar
-]]>
-
-
-
-
-
- An informational response using a 1xx status code other than 101 is transmitted as a
- HEADERS frame, followed by zero or more CONTINUATION
- frames:
-
- - END_STREAM
- + END_HEADERS
- :status = 103
- extension-field = bar
-]]>
-
-
-
-
-
- In HTTP/1.1, an HTTP client is unable to retry a non-idempotent request when an error
- occurs, because there is no means to determine the nature of the error. It is possible
- that some server processing occurred prior to the error, which could result in
- undesirable effects if the request were reattempted.
-
-
- HTTP/2 provides two mechanisms for providing a guarantee to a client that a request has
- not been processed:
-
-
- The GOAWAY frame indicates the highest stream number that might have
- been processed. Requests on streams with higher numbers are therefore guaranteed to
- be safe to retry.
-
-
- The REFUSED_STREAM error code can be included in a
- RST_STREAM frame to indicate that the stream is being closed prior to
- any processing having occurred. Any request that was sent on the reset stream can
- be safely retried.
-
-
-
-
- Requests that have not been processed have not failed; clients MAY automatically retry
- them, even those with non-idempotent methods.
-
-
- A server MUST NOT indicate that a stream has not been processed unless it can guarantee
- that fact. If frames that are on a stream are passed to the application layer for any
- stream, then REFUSED_STREAM MUST NOT be used for that stream, and a
- GOAWAY frame MUST include a stream identifier that is greater than or
- equal to the given stream identifier.
-
-
- In addition to these mechanisms, the PING frame provides a way for a
- client to easily test a connection. Connections that remain idle can become broken as
- some middleboxes (for instance, network address translators, or load balancers) silently
- discard connection bindings. The PING frame allows a client to safely
- test whether a connection is still active without sending a request.
-
-
-
-
-
-
- HTTP/2 allows a server to pre-emptively send (or "push") responses (along with
- corresponding "promised" requests) to a client in association with a previous
- client-initiated request. This can be useful when the server knows the client will need
- to have those responses available in order to fully process the response to the original
- request.
-
-
-
- Pushing additional message exchanges in this fashion is optional, and is negotiated
- between individual endpoints. The SETTINGS_ENABLE_PUSH setting can be set
- to 0 to indicate that server push is disabled.
-
-
- Promised requests MUST be cacheable (see ), MUST be safe (see ) and MUST NOT include a request body. Clients that receive a
- promised request that is not cacheable, unsafe or that includes a request body MUST
- reset the stream with a stream error of type
- PROTOCOL_ERROR.
-
-
- Pushed responses that are cacheable (see ) can be stored by the client, if it implements a HTTP
- cache. Pushed responses are considered successfully validated on the origin server (e.g.,
- if the "no-cache" cache response directive is present) while the stream identified by the
- promised stream ID is still open.
-
-
- Pushed responses that are not cacheable MUST NOT be stored by any HTTP cache. They MAY
- be made available to the application separately.
-
-
- An intermediary can receive pushes from the server and choose not to forward them on to
- the client. In other words, how to make use of the pushed information is up to that
- intermediary. Equally, the intermediary might choose to make additional pushes to the
- client, without any action taken by the server.
-
-
- A client cannot push. Thus, servers MUST treat the receipt of a
- PUSH_PROMISE frame as a connection
- error of type PROTOCOL_ERROR. Clients MUST reject any attempt to
- change the SETTINGS_ENABLE_PUSH setting to a value other than 0 by treating
- the message as a connection error of type
- PROTOCOL_ERROR.
-
-
-
-
- Server push is semantically equivalent to a server responding to a request; however, in
- this case that request is also sent by the server, as a PUSH_PROMISE
- frame.
-
-
- The PUSH_PROMISE frame includes a header block that contains a complete
- set of request header fields that the server attributes to the request. It is not
- possible to push a response to a request that includes a request body.
-
-
-
- Pushed responses are always associated with an explicit request from the client. The
- PUSH_PROMISE frames sent by the server are sent on that explicit
- request's stream. The PUSH_PROMISE frame also includes a promised stream
- identifier, chosen from the stream identifiers available to the server (see ).
-
-
-
- The header fields in PUSH_PROMISE and any subsequent
- CONTINUATION frames MUST be a valid and complete set of request header fields. The server MUST include a method in
- the :method header field that is safe and cacheable. If a
- client receives a PUSH_PROMISE that does not include a complete and valid
- set of header fields, or the :method header field identifies
- a method that is not safe, it MUST respond with a stream error of type PROTOCOL_ERROR.
-
-
-
- The server SHOULD send PUSH_PROMISE ()
- frames prior to sending any frames that reference the promised responses. This avoids a
- race where clients issue requests prior to receiving any PUSH_PROMISE
- frames.
-
-
- For example, if the server receives a request for a document containing embedded links
- to multiple image files, and the server chooses to push those additional images to the
- client, sending push promises before the DATA frames that contain the
- image links ensures that the client is able to see the promises before discovering
- embedded links. Similarly, if the server pushes responses referenced by the header block
- (for instance, in Link header fields), sending the push promises before sending the
- header block ensures that clients do not request them.
-
-
-
- PUSH_PROMISE frames MUST NOT be sent by the client.
-
-
- PUSH_PROMISE frames can be sent by the server in response to any
- client-initiated stream, but the stream MUST be in either the "open" or "half closed
- (remote)" state with respect to the server. PUSH_PROMISE frames are
- interspersed with the frames that comprise a response, though they cannot be
- interspersed with HEADERS and CONTINUATION frames that
- comprise a single header block.
-
-
- Sending a PUSH_PROMISE frame creates a new stream and puts the stream
- into the “reserved (local)” state for the server and the “reserved (remote)” state for
- the client.
-
-
-
-
-
- After sending the PUSH_PROMISE frame, the server can begin delivering the
- pushed response as a response on a server-initiated
- stream that uses the promised stream identifier. The server uses this stream to
- transmit an HTTP response, using the same sequence of frames as defined in . This stream becomes "half closed"
- to the client after the initial HEADERS frame is sent.
-
-
-
- Once a client receives a PUSH_PROMISE frame and chooses to accept the
- pushed response, the client SHOULD NOT issue any requests for the promised response
- until after the promised stream has closed.
-
-
-
- If the client determines, for any reason, that it does not wish to receive the pushed
- response from the server, or if the server takes too long to begin sending the promised
- response, the client can send an RST_STREAM frame, using either the
- CANCEL or REFUSED_STREAM codes, and referencing the pushed
- stream's identifier.
-
-
- A client can use the SETTINGS_MAX_CONCURRENT_STREAMS setting to limit the
- number of responses that can be concurrently pushed by a server. Advertising a
- SETTINGS_MAX_CONCURRENT_STREAMS value of zero disables server push by
- preventing the server from creating the necessary streams. This does not prohibit a
- server from sending PUSH_PROMISE frames; clients need to reset any
- promised streams that are not wanted.
-
-
-
- Clients receiving a pushed response MUST validate that either the server is
- authoritative (see ), or the proxy that provided the pushed
- response is configured for the corresponding request. For example, a server that offers
- a certificate for only the example.com DNS-ID or Common Name
- is not permitted to push a response for https://www.example.org/doc.
-
-
- The response for a PUSH_PROMISE stream begins with a
- HEADERS frame, which immediately puts the stream into the “half closed
- (remote)” state for the server and “half closed (local)” state for the client, and ends
- with a frame bearing END_STREAM, which places the stream in the "closed" state.
-
-
- The client never sends a frame with the END_STREAM flag for a server push.
-
-
-
-
-
-
-
-
-
- In HTTP/1.x, the pseudo-method CONNECT () is used to convert an HTTP connection into a tunnel to a remote host.
- CONNECT is primarily used with HTTP proxies to establish a TLS session with an origin
- server for the purposes of interacting with https resources.
-
-
- In HTTP/2, the CONNECT method is used to establish a tunnel over a single HTTP/2 stream to
- a remote host, for similar purposes. The HTTP header field mapping works as defined in
- Request Header Fields, with a few
- differences. Specifically:
-
-
- The :method header field is set to CONNECT.
-
-
- The :scheme and :path header
- fields MUST be omitted.
-
-
- The :authority header field contains the host and port to
- connect to (equivalent to the authority-form of the request-target of CONNECT
- requests, see ).
-
-
-
-
- A proxy that supports CONNECT establishes a TCP connection to
- the server identified in the :authority header field. Once
- this connection is successfully established, the proxy sends a HEADERS
- frame containing a 2xx series status code to the client, as defined in .
-
-
- After the initial HEADERS frame sent by each peer, all subsequent
- DATA frames correspond to data sent on the TCP connection. The payload of
- any DATA frames sent by the client is transmitted by the proxy to the TCP
- server; data received from the TCP server is assembled into DATA frames by
- the proxy. Frame types other than DATA or stream management frames
- (RST_STREAM, WINDOW_UPDATE, and PRIORITY)
- MUST NOT be sent on a connected stream, and MUST be treated as a stream error if received.
-
-
- The TCP connection can be closed by either peer. The END_STREAM flag on a
- DATA frame is treated as being equivalent to the TCP FIN bit. A client is
- expected to send a DATA frame with the END_STREAM flag set after receiving
- a frame bearing the END_STREAM flag. A proxy that receives a DATA frame
- with the END_STREAM flag set sends the attached data with the FIN bit set on the last TCP
- segment. A proxy that receives a TCP segment with the FIN bit set sends a
- DATA frame with the END_STREAM flag set. Note that the final TCP segment
- or DATA frame could be empty.
-
-
- A TCP connection error is signaled with RST_STREAM. A proxy treats any
- error in the TCP connection, which includes receiving a TCP segment with the RST bit set,
- as a stream error of type
- CONNECT_ERROR. Correspondingly, a proxy MUST send a TCP segment with the
- RST bit set if it detects an error with the stream or the HTTP/2 connection.
-
-
-
-
-
-
- This section outlines attributes of the HTTP protocol that improve interoperability, reduce
- exposure to known security vulnerabilities, or reduce the potential for implementation
- variation.
-
-
-
-
- HTTP/2 connections are persistent. For best performance, it is expected clients will not
- close connections until it is determined that no further communication with a server is
- necessary (for example, when a user navigates away from a particular web page), or until
- the server closes the connection.
-
-
- Clients SHOULD NOT open more than one HTTP/2 connection to a given host and port pair,
- where host is derived from a URI, a selected alternative
- service, or a configured proxy.
-
-
- A client can create additional connections as replacements, either to replace connections
- that are near to exhausting the available stream
- identifier space, to refresh the keying material for a TLS connection, or to
- replace connections that have encountered errors.
-
-
- A client MAY open multiple connections to the same IP address and TCP port using different
- Server Name Indication values or to provide different TLS
- client certificates, but SHOULD avoid creating multiple connections with the same
- configuration.
-
-
- Servers are encouraged to maintain open connections for as long as possible, but are
- permitted to terminate idle connections if necessary. When either endpoint chooses to
- close the transport-layer TCP connection, the terminating endpoint SHOULD first send a
- GOAWAY () frame so that both endpoints can reliably
- determine whether previously sent frames have been processed and gracefully complete or
- terminate any necessary remaining tasks.
-
-
-
-
- Connections that are made to an origin servers, either directly or through a tunnel
- created using the CONNECT method MAY be reused for
- requests with multiple different URI authority components. A connection can be reused
- as long as the origin server is authoritative. For
- http resources, this depends on the host having resolved to
- the same IP address.
-
-
- For https resources, connection reuse additionally depends
- on having a certificate that is valid for the host in the URI. An origin server might
- offer a certificate with multiple subjectAltName attributes,
- or names with wildcards, one of which is valid for the authority in the URI. For
- example, a certificate with a subjectAltName of *.example.com might permit the use of the same connection for
- requests to URIs starting with https://a.example.com/ and
- https://b.example.com/.
-
-
- In some deployments, reusing a connection for multiple origins can result in requests
- being directed to the wrong origin server. For example, TLS termination might be
- performed by a middlebox that uses the TLS Server Name Indication
- (SNI) extension to select an origin server. This means that it is possible
- for clients to send confidential information to servers that might not be the intended
- target for the request, even though the server is otherwise authoritative.
-
-
- A server that does not wish clients to reuse connections can indicate that it is not
- authoritative for a request by sending a 421 (Misdirected Request) status code in response
- to the request (see ).
-
-
- A client that is configured to use a proxy over HTTP/2 directs requests to that proxy
- through a single connection. That is, all requests sent via a proxy reuse the
- connection to the proxy.
-
-
-
-
-
- The 421 (Misdirected Request) status code indicates that the request was directed at a
- server that is not able to produce a response. This can be sent by a server that is not
- configured to produce responses for the combination of scheme and authority that are
- included in the request URI.
-
-
- Clients receiving a 421 (Misdirected Request) response from a server MAY retry the
- request - whether the request method is idempotent or not - over a different connection.
- This is possible if a connection is reused () or if an alternative
- service is selected ().
-
-
- This status code MUST NOT be generated by proxies.
-
-
- A 421 response is cacheable by default; i.e., unless otherwise indicated by the method
- definition or explicit cache controls (see ).
-
-
-
-
-
-
- Implementations of HTTP/2 MUST support TLS 1.2 for HTTP/2 over
- TLS. The general TLS usage guidance in SHOULD be followed, with
- some additional restrictions that are specific to HTTP/2.
-
-
-
- An implementation of HTTP/2 over TLS MUST use TLS 1.2 or higher with the restrictions on
- feature set and cipher suite described in this section. Due to implementation
- limitations, it might not be possible to fail TLS negotiation. An endpoint MUST
- immediately terminate an HTTP/2 connection that does not meet these minimum requirements
- with a connection error of type
- INADEQUATE_SECURITY.
-
-
-
-
- The TLS implementation MUST support the Server Name Indication
- (SNI) extension to TLS. HTTP/2 clients MUST indicate the target domain name when
- negotiating TLS.
-
-
- The TLS implementation MUST disable compression. TLS compression can lead to the
- exposure of information that would not otherwise be revealed .
- Generic compression is unnecessary since HTTP/2 provides compression features that are
- more aware of context and therefore likely to be more appropriate for use for
- performance, security or other reasons.
-
-
- The TLS implementation MUST disable renegotiation. An endpoint MUST treat a TLS
- renegotiation as a connection error of type
- PROTOCOL_ERROR. Note that disabling renegotiation can result in
- long-lived connections becoming unusable due to limits on the number of messages the
- underlying cipher suite can encipher.
-
-
- A client MAY use renegotiation to provide confidentiality protection for client
- credentials offered in the handshake, but any renegotiation MUST occur prior to sending
- the connection preface. A server SHOULD request a client certificate if it sees a
- renegotiation request immediately after establishing a connection.
-
-
- This effectively prevents the use of renegotiation in response to a request for a
- specific protected resource. A future specification might provide a way to support this
- use case.
-
-
-
-
-
- The set of TLS cipher suites that are permitted in HTTP/2 is restricted. HTTP/2 MUST
- only be used with cipher suites that have ephemeral key exchange, such as the ephemeral Diffie-Hellman (DHE) or the elliptic curve variant (ECDHE). Ephemeral key exchange MUST
- have a minimum size of 2048 bits for DHE or security level of 128 bits for ECDHE.
- Clients MUST accept DHE sizes of up to 4096 bits. HTTP MUST NOT be used with cipher
- suites that use stream or block ciphers. Authenticated Encryption with Additional Data
- (AEAD) modes, such as the Galois Counter Model (GCM) mode for
- AES are acceptable.
-
-
- The effect of these restrictions is that TLS 1.2 implementations could have
- non-intersecting sets of available cipher suites, since these prevent the use of the
- cipher suite that TLS 1.2 makes mandatory. To avoid this problem, implementations of
- HTTP/2 that use TLS 1.2 MUST support TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 with P256 .
-
-
- Clients MAY advertise support of cipher suites that are prohibited by the above
- restrictions in order to allow for connection to servers that do not support HTTP/2.
- This enables a fallback to protocols without these constraints without the additional
- latency imposed by using a separate connection for fallback.
-
-
-
-
-
-
-
-
- HTTP/2 relies on the HTTP/1.1 definition of authority for determining whether a server is
- authoritative in providing a given response, see . This relies on local name resolution for the "http"
- URI scheme, and the authenticated server identity for the "https" scheme (see ).
-
-
-
-
-
- In a cross-protocol attack, an attacker causes a client to initiate a transaction in one
- protocol toward a server that understands a different protocol. An attacker might be able
- to cause the transaction to appear as valid transaction in the second protocol. In
- combination with the capabilities of the web context, this can be used to interact with
- poorly protected servers in private networks.
-
-
- Completing a TLS handshake with an ALPN identifier for HTTP/2 can be considered sufficient
- protection against cross protocol attacks. ALPN provides a positive indication that a
- server is willing to proceed with HTTP/2, which prevents attacks on other TLS-based
- protocols.
-
-
- The encryption in TLS makes it difficult for attackers to control the data which could be
- used in a cross-protocol attack on a cleartext protocol.
-
-
- The cleartext version of HTTP/2 has minimal protection against cross-protocol attacks.
- The connection preface contains a string that is
- designed to confuse HTTP/1.1 servers, but no special protection is offered for other
- protocols. A server that is willing to ignore parts of an HTTP/1.1 request containing an
- Upgrade header field in addition to the client connection preface could be exposed to a
- cross-protocol attack.
-
-
-
-
-
- HTTP/2 header field names and values are encoded as sequences of octets with a length
- prefix. This enables HTTP/2 to carry any string of octets as the name or value of a
- header field. An intermediary that translates HTTP/2 requests or responses into HTTP/1.1
- directly could permit the creation of corrupted HTTP/1.1 messages. An attacker might
- exploit this behavior to cause the intermediary to create HTTP/1.1 messages with illegal
- header fields, extra header fields, or even new messages that are entirely falsified.
-
-
- Header field names or values that contain characters not permitted by HTTP/1.1, including
- carriage return (ASCII 0xd) or line feed (ASCII 0xa) MUST NOT be translated verbatim by an
- intermediary, as stipulated in .
-
-
- Translation from HTTP/1.x to HTTP/2 does not produce the same opportunity to an attacker.
- Intermediaries that perform translation to HTTP/2 MUST remove any instances of the obs-fold production from header field values.
-
-
-
-
-
- Pushed responses do not have an explicit request from the client; the request
- is provided by the server in the PUSH_PROMISE frame.
-
-
- Caching responses that are pushed is possible based on the guidance provided by the origin
- server in the Cache-Control header field. However, this can cause issues if a single
- server hosts more than one tenant. For example, a server might offer multiple users each
- a small portion of its URI space.
-
-
- Where multiple tenants share space on the same server, that server MUST ensure that
- tenants are not able to push representations of resources that they do not have authority
- over. Failure to enforce this would allow a tenant to provide a representation that would
- be served out of cache, overriding the actual representation that the authoritative tenant
- provides.
-
-
- Pushed responses for which an origin server is not authoritative (see
- ) are never cached or used.
-
-
-
-
-
- An HTTP/2 connection can demand a greater commitment of resources to operate than a
- HTTP/1.1 connection. The use of header compression and flow control depend on a
- commitment of resources for storing a greater amount of state. Settings for these
- features ensure that memory commitments for these features are strictly bounded.
-
-
- The number of PUSH_PROMISE frames is not constrained in the same fashion.
- A client that accepts server push SHOULD limit the number of streams it allows to be in
- the "reserved (remote)" state. Excessive number of server push streams can be treated as
- a stream error of type
- ENHANCE_YOUR_CALM.
-
-
- Processing capacity cannot be guarded as effectively as state capacity.
-
-
- The SETTINGS frame can be abused to cause a peer to expend additional
- processing time. This might be done by pointlessly changing SETTINGS parameters, setting
- multiple undefined parameters, or changing the same setting multiple times in the same
- frame. WINDOW_UPDATE or PRIORITY frames can be abused to
- cause an unnecessary waste of resources.
-
-
- Large numbers of small or empty frames can be abused to cause a peer to expend time
- processing frame headers. Note however that some uses are entirely legitimate, such as
- the sending of an empty DATA frame to end a stream.
-
-
- Header compression also offers some opportunities to waste processing resources; see for more details on potential abuses.
-
-
- Limits in SETTINGS parameters cannot be reduced instantaneously, which
- leaves an endpoint exposed to behavior from a peer that could exceed the new limits. In
- particular, immediately after establishing a connection, limits set by a server are not
- known to clients and could be exceeded without being an obvious protocol violation.
-
-
- All these features - i.e., SETTINGS changes, small frames, header
- compression - have legitimate uses. These features become a burden only when they are
- used unnecessarily or to excess.
-
-
- An endpoint that doesn't monitor this behavior exposes itself to a risk of denial of
- service attack. Implementations SHOULD track the use of these features and set limits on
- their use. An endpoint MAY treat activity that is suspicious as a connection error of type
- ENHANCE_YOUR_CALM.
-
-
-
-
- A large header block can cause an implementation to
- commit a large amount of state. Header fields that are critical for routing can appear
- toward the end of a header block, which prevents streaming of header fields to their
- ultimate destination. For this an other reasons, such as ensuring cache correctness,
- means that an endpoint might need to buffer the entire header block. Since there is no
- hard limit to the size of a header block, some endpoints could be forced commit a large
- amount of available memory for header fields.
-
-
- An endpoint can use the SETTINGS_MAX_HEADER_LIST_SIZE to advise peers of
- limits that might apply on the size of header blocks. This setting is only advisory, so
- endpoints MAY choose to send header blocks that exceed this limit and risk having the
- request or response being treated as malformed. This setting specific to a connection,
- so any request or response could encounter a hop with a lower, unknown limit. An
- intermediary can attempt to avoid this problem by passing on values presented by
- different peers, but they are not obligated to do so.
-
-
- A server that receives a larger header block than it is willing to handle can send an
- HTTP 431 (Request Header Fields Too Large) status code . A
- client can discard responses that it cannot process. The header block MUST be processed
- to ensure a consistent connection state, unless the connection is closed.
-
-
-
-
-
-
- HTTP/2 enables greater use of compression for both header fields () and entity bodies. Compression can allow an attacker to recover
- secret data when it is compressed in the same context as data under attacker control.
-
-
- There are demonstrable attacks on compression that exploit the characteristics of the web
- (e.g., ). The attacker induces multiple requests containing
- varying plaintext, observing the length of the resulting ciphertext in each, which
- reveals a shorter length when a guess about the secret is correct.
-
-
- Implementations communicating on a secure channel MUST NOT compress content that includes
- both confidential and attacker-controlled data unless separate compression dictionaries
- are used for each source of data. Compression MUST NOT be used if the source of data
- cannot be reliably determined. Generic stream compression, such as that provided by TLS
- MUST NOT be used with HTTP/2 ().
-
-
- Further considerations regarding the compression of header fields are described in .
-
-
-
-
-
- Padding within HTTP/2 is not intended as a replacement for general purpose padding, such
- as might be provided by TLS. Redundant padding could even be
- counterproductive. Correct application can depend on having specific knowledge of the
- data that is being padded.
-
-
- To mitigate attacks that rely on compression, disabling or limiting compression might be
- preferable to padding as a countermeasure.
-
-
- Padding can be used to obscure the exact size of frame content, and is provided to
- mitigate specific attacks within HTTP. For example, attacks where compressed content
- includes both attacker-controlled plaintext and secret data (see for example, ).
-
-
- Use of padding can result in less protection than might seem immediately obvious. At
- best, padding only makes it more difficult for an attacker to infer length information by
- increasing the number of frames an attacker has to observe. Incorrectly implemented
- padding schemes can be easily defeated. In particular, randomized padding with a
- predictable distribution provides very little protection; similarly, padding payloads to a
- fixed size exposes information as payload sizes cross the fixed size boundary, which could
- be possible if an attacker can control plaintext.
-
-
- Intermediaries SHOULD retain padding for DATA frames, but MAY drop padding
- for HEADERS and PUSH_PROMISE frames. A valid reason for an
- intermediary to change the amount of padding of frames is to improve the protections that
- padding provides.
-
-
-
-
-
- Several characteristics of HTTP/2 provide an observer an opportunity to correlate actions
- of a single client or server over time. This includes the value of settings, the manner
- in which flow control windows are managed, the way priorities are allocated to streams,
- timing of reactions to stimulus, and handling of any optional features.
-
-
- As far as this creates observable differences in behavior, they could be used as a basis
- for fingerprinting a specific client, as defined in .
-
-
-
-
-
-
- A string for identifying HTTP/2 is entered into the "Application Layer Protocol Negotiation
- (ALPN) Protocol IDs" registry established in .
-
-
- This document establishes a registry for frame types, settings, and error codes. These new
- registries are entered into a new "Hypertext Transfer Protocol (HTTP) 2 Parameters" section.
-
-
- This document registers the HTTP2-Settings header field for
- use in HTTP; and the 421 (Misdirected Request) status code.
-
-
- This document registers the PRI method for use in HTTP, to avoid
- collisions with the connection preface.
-
-
-
-
- This document creates two registrations for the identification of HTTP/2 in the
- "Application Layer Protocol Negotiation (ALPN) Protocol IDs" registry established in .
-
-
- The "h2" string identifies HTTP/2 when used over TLS:
-
- HTTP/2 over TLS
- 0x68 0x32 ("h2")
- This document
-
-
-
- The "h2c" string identifies HTTP/2 when used over cleartext TCP:
-
- HTTP/2 over TCP
- 0x68 0x32 0x63 ("h2c")
- This document
-
-
-
-
-
-
- This document establishes a registry for HTTP/2 frame type codes. The "HTTP/2 Frame
- Type" registry manages an 8-bit space. The "HTTP/2 Frame Type" registry operates under
- either of the "IETF Review" or "IESG Approval" policies for
- values between 0x00 and 0xef, with values between 0xf0 and 0xff being reserved for
- experimental use.
-
-
- New entries in this registry require the following information:
-
-
- A name or label for the frame type.
-
-
- The 8-bit code assigned to the frame type.
-
-
- A reference to a specification that includes a description of the frame layout,
- it's semantics and flags that the frame type uses, including any parts of the frame
- that are conditionally present based on the value of flags.
-
-
-
-
- The entries in the following table are registered by this document.
-
-
- Frame Type
- Code
- Section
- DATA0x0
- HEADERS0x1
- PRIORITY0x2
- RST_STREAM0x3
- SETTINGS0x4
- PUSH_PROMISE0x5
- PING0x6
- GOAWAY0x7
- WINDOW_UPDATE0x8
- CONTINUATION0x9
-
-
-
-
-
- This document establishes a registry for HTTP/2 settings. The "HTTP/2 Settings" registry
- manages a 16-bit space. The "HTTP/2 Settings" registry operates under the "Expert Review" policy for values in the range from 0x0000 to
- 0xefff, with values between and 0xf000 and 0xffff being reserved for experimental use.
-
-
- New registrations are advised to provide the following information:
-
-
- A symbolic name for the setting. Specifying a setting name is optional.
-
-
- The 16-bit code assigned to the setting.
-
-
- An initial value for the setting.
-
-
- An optional reference to a specification that describes the use of the setting.
-
-
-
-
- An initial set of setting registrations can be found in .
-
-
- Name
- Code
- Initial Value
- Specification
- HEADER_TABLE_SIZE
- 0x14096
- ENABLE_PUSH
- 0x21
- MAX_CONCURRENT_STREAMS
- 0x3(infinite)
- INITIAL_WINDOW_SIZE
- 0x465535
- MAX_FRAME_SIZE
- 0x516384
- MAX_HEADER_LIST_SIZE
- 0x6(infinite)
-
-
-
-
-
-
- This document establishes a registry for HTTP/2 error codes. The "HTTP/2 Error Code"
- registry manages a 32-bit space. The "HTTP/2 Error Code" registry operates under the
- "Expert Review" policy.
-
-
- Registrations for error codes are required to include a description of the error code. An
- expert reviewer is advised to examine new registrations for possible duplication with
- existing error codes. Use of existing registrations is to be encouraged, but not
- mandated.
-
-
- New registrations are advised to provide the following information:
-
-
- A name for the error code. Specifying an error code name is optional.
-
-
- The 32-bit error code value.
-
-
- A brief description of the error code semantics, longer if no detailed specification
- is provided.
-
-
- An optional reference for a specification that defines the error code.
-
-
-
-
- The entries in the following table are registered by this document.
-
-
- Name
- Code
- Description
- Specification
- NO_ERROR0x0
- Graceful shutdown
-
- PROTOCOL_ERROR0x1
- Protocol error detected
-
- INTERNAL_ERROR0x2
- Implementation fault
-
- FLOW_CONTROL_ERROR0x3
- Flow control limits exceeded
-
- SETTINGS_TIMEOUT0x4
- Settings not acknowledged
-
- STREAM_CLOSED0x5
- Frame received for closed stream
-
- FRAME_SIZE_ERROR0x6
- Frame size incorrect
-
- REFUSED_STREAM0x7
- Stream not processed
-
- CANCEL0x8
- Stream cancelled
-
- COMPRESSION_ERROR0x9
- Compression state not updated
-
- CONNECT_ERROR0xa
- TCP connection error for CONNECT method
-
- ENHANCE_YOUR_CALM0xb
- Processing capacity exceeded
-
- INADEQUATE_SECURITY0xc
- Negotiated TLS parameters not acceptable
-
-
-
-
-
-
-
- This section registers the HTTP2-Settings header field in the
- Permanent Message Header Field Registry.
-
-
- HTTP2-Settings
-
-
- http
-
-
- standard
-
-
- IETF
-
-
- of this document
-
-
- This header field is only used by an HTTP/2 client for Upgrade-based negotiation.
-
-
-
-
-
-
-
- This section registers the PRI method in the HTTP Method
- Registry ().
-
-
- PRI
-
-
- No
-
-
- No
-
-
- of this document
-
-
- This method is never used by an actual client. This method will appear to be used
- when an HTTP/1.1 server or intermediary attempts to parse an HTTP/2 connection
- preface.
-
-
-
-
-
-
-
- This document registers the 421 (Misdirected Request) HTTP Status code in the Hypertext
- Transfer Protocol (HTTP) Status Code Registry ().
-
-
-
-
- 421
-
-
- Misdirected Request
-
-
- of this document
-
-
-
-
-
-
-
-
-
- This document includes substantial input from the following individuals:
-
-
- Adam Langley, Wan-Teh Chang, Jim Morrison, Mark Nottingham, Alyssa Wilk, Costin
- Manolache, William Chan, Vitaliy Lvin, Joe Chan, Adam Barth, Ryan Hamilton, Gavin
- Peters, Kent Alstad, Kevin Lindsay, Paul Amer, Fan Yang, Jonathan Leighton (SPDY
- contributors).
-
-
- Gabriel Montenegro and Willy Tarreau (Upgrade mechanism).
-
-
- William Chan, Salvatore Loreto, Osama Mazahir, Gabriel Montenegro, Jitu Padhye, Roberto
- Peon, Rob Trace (Flow control).
-
-
- Mike Bishop (Extensibility).
-
-
- Mark Nottingham, Julian Reschke, James Snell, Jeff Pinner, Mike Bishop, Herve Ruellan
- (Substantial editorial contributions).
-
-
- Kari Hurtta, Tatsuhiro Tsujikawa, Greg Wilkins, Poul-Henning Kamp.
-
-
- Alexey Melnikov was an editor of this document during 2013.
-
-
- A substantial proportion of Martin's contribution was supported by Microsoft during his
- employment there.
-
-
-
-
-
-
-
-
-
-
- HPACK - Header Compression for HTTP/2
-
-
-
-
-
-
-
-
-
-
-
- Transmission Control Protocol
-
-
- University of Southern California (USC)/Information Sciences
- Institute
-
-
-
-
-
-
-
-
-
-
- Key words for use in RFCs to Indicate Requirement Levels
-
-
- Harvard University
- sob@harvard.edu
-
-
-
-
-
-
-
-
-
-
- HTTP Over TLS
-
-
-
-
-
-
-
-
-
- Uniform Resource Identifier (URI): Generic
- Syntax
-
-
-
-
-
-
-
-
-
-
-
- The Base16, Base32, and Base64 Data Encodings
-
-
-
-
-
-
-
-
- Guidelines for Writing an IANA Considerations Section in RFCs
-
-
-
-
-
-
-
-
-
-
- Augmented BNF for Syntax Specifications: ABNF
-
-
-
-
-
-
-
-
-
-
- The Transport Layer Security (TLS) Protocol Version 1.2
-
-
-
-
-
-
-
-
-
-
- Transport Layer Security (TLS) Extensions: Extension Definitions
-
-
-
-
-
-
-
-
-
- Transport Layer Security (TLS) Application-Layer Protocol Negotiation Extension
-
-
-
-
-
-
-
-
-
-
-
-
- TLS Elliptic Curve Cipher Suites with SHA-256/384 and AES Galois
- Counter Mode (GCM)
-
-
-
-
-
-
-
-
-
-
- Digital Signature Standard (DSS)
-
- NIST
-
-
-
-
-
-
-
-
- Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing
-
- Adobe Systems Incorporated
- fielding@gbiv.com
-
-
- greenbytes GmbH
- julian.reschke@greenbytes.de
-
-
-
-
-
-
-
-
-
- Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content
-
- Adobe Systems Incorporated
- fielding@gbiv.com
-
-
- greenbytes GmbH
- julian.reschke@greenbytes.de
-
-
-
-
-
-
-
-
- Hypertext Transfer Protocol (HTTP/1.1): Conditional Requests
-
- Adobe Systems Incorporated
- fielding@gbiv.com
-
-
- greenbytes GmbH
- julian.reschke@greenbytes.de
-
-
-
-
-
-
-
- Hypertext Transfer Protocol (HTTP/1.1): Range Requests
-
- Adobe Systems Incorporated
- fielding@gbiv.com
-
-
- World Wide Web Consortium
- ylafon@w3.org
-
-
- greenbytes GmbH
- julian.reschke@greenbytes.de
-
-
-
-
-
-
-
- Hypertext Transfer Protocol (HTTP/1.1): Caching
-
- Adobe Systems Incorporated
- fielding@gbiv.com
-
-
- Akamai
- mnot@mnot.net
-
-
- greenbytes GmbH
- julian.reschke@greenbytes.de
-
-
-
-
-
-
-
-
- Hypertext Transfer Protocol (HTTP/1.1): Authentication
-
- Adobe Systems Incorporated
- fielding@gbiv.com
-
-
- greenbytes GmbH
- julian.reschke@greenbytes.de
-
-
-
-
-
-
-
-
-
- HTTP State Management Mechanism
-
-
-
-
-
-
-
-
-
-
-
- TCP Extensions for High Performance
-
-
-
-
-
-
-
-
-
-
-
- Transport Layer Security Protocol Compression Methods
-
-
-
-
-
-
-
-
- Additional HTTP Status Codes
-
-
-
-
-
-
-
-
-
-
- Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- AES Galois Counter Mode (GCM) Cipher Suites for TLS
-
-
-
-
-
-
-
-
-
-
-
- HTML5
-
-
-
-
-
-
-
-
-
-
- Latest version available at
- .
-
-
-
-
-
-
- Talking to Yourself for Fun and Profit
-
-
-
-
-
-
-
-
-
-
-
-
-
- BREACH: Reviving the CRIME Attack
-
-
-
-
-
-
-
-
-
-
- Registration Procedures for Message Header Fields
-
- Nine by Nine
- GK-IETF@ninebynine.org
-
-
- BEA Systems
- mnot@pobox.com
-
-
- HP Labs
- JeffMogul@acm.org
-
-
-
-
-
-
-
-
-
- Recommendations for Secure Use of TLS and DTLS
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- HTTP Alternative Services
-
-
- Akamai
-
-
- Mozilla
-
-
- greenbytes
-
-
-
-
-
-
-
-
-
-
- This section is to be removed by RFC Editor before publication.
-
-
-
-
- Renamed Not Authoritative status code to Misdirected Request.
-
-
-
-
-
- Pseudo-header fields are now required to appear strictly before regular ones.
-
-
- Restored 1xx series status codes, except 101.
-
-
- Changed frame length field 24-bits. Expanded frame header to 9 octets. Added a setting
- to limit the damage.
-
-
- Added a setting to advise peers of header set size limits.
-
-
- Removed segments.
-
-
- Made non-semantic-bearing HEADERS frames illegal in the HTTP mapping.
-
-
-
-
-
- Restored extensibility options.
-
-
- Restricting TLS cipher suites to AEAD only.
-
-
- Removing Content-Encoding requirements.
-
-
- Permitting the use of PRIORITY after stream close.
-
-
- Removed ALTSVC frame.
-
-
- Removed BLOCKED frame.
-
-
- Reducing the maximum padding size to 256 octets; removing padding from
- CONTINUATION frames.
-
-
- Removed per-frame GZIP compression.
-
-
-
-
-
- Added BLOCKED frame (at risk).
-
-
- Simplified priority scheme.
-
-
- Added DATA per-frame GZIP compression.
-
-
-
-
-
- Changed "connection header" to "connection preface" to avoid confusion.
-
-
- Added dependency-based stream prioritization.
-
-
- Added "h2c" identifier to distinguish between cleartext and secured HTTP/2.
-
-
- Adding missing padding to PUSH_PROMISE.
-
-
- Integrate ALTSVC frame and supporting text.
-
-
- Dropping requirement on "deflate" Content-Encoding.
-
-
- Improving security considerations around use of compression.
-
-
-
-
-
- Adding padding for data frames.
-
-
- Renumbering frame types, error codes, and settings.
-
-
- Adding INADEQUATE_SECURITY error code.
-
-
- Updating TLS usage requirements to 1.2; forbidding TLS compression.
-
-
- Removing extensibility for frames and settings.
-
-
- Changing setting identifier size.
-
-
- Removing the ability to disable flow control.
-
-
- Changing the protocol identification token to "h2".
-
-
- Changing the use of :authority to make it optional and to allow userinfo in non-HTTP
- cases.
-
-
- Allowing split on 0x0 for Cookie.
-
-
- Reserved PRI method in HTTP/1.1 to avoid possible future collisions.
-
-
-
-
-
- Added cookie crumbling for more efficient header compression.
-
-
- Added header field ordering with the value-concatenation mechanism.
-
-
-
-
-
- Marked draft for implementation.
-
-
-
-
-
- Adding definition for CONNECT method.
-
-
- Constraining the use of push to safe, cacheable methods with no request body.
-
-
- Changing from :host to :authority to remove any potential confusion.
-
-
- Adding setting for header compression table size.
-
-
- Adding settings acknowledgement.
-
-
- Removing unnecessary and potentially problematic flags from CONTINUATION.
-
-
- Added denial of service considerations.
-
-
-
-
- Marking the draft ready for implementation.
-
-
- Renumbering END_PUSH_PROMISE flag.
-
-
- Editorial clarifications and changes.
-
-
-
-
-
- Added CONTINUATION frame for HEADERS and PUSH_PROMISE.
-
-
- PUSH_PROMISE is no longer implicitly prohibited if SETTINGS_MAX_CONCURRENT_STREAMS is
- zero.
-
-
- Push expanded to allow all safe methods without a request body.
-
-
- Clarified the use of HTTP header fields in requests and responses. Prohibited HTTP/1.1
- hop-by-hop header fields.
-
-
- Requiring that intermediaries not forward requests with missing or illegal routing
- :-headers.
-
-
- Clarified requirements around handling different frames after stream close, stream reset
- and GOAWAY.
-
-
- Added more specific prohibitions for sending of different frame types in various stream
- states.
-
-
- Making the last received setting value the effective value.
-
-
- Clarified requirements on TLS version, extension and ciphers.
-
-
-
-
-
- Committed major restructuring atrocities.
-
-
- Added reference to first header compression draft.
-
-
- Added more formal description of frame lifecycle.
-
-
- Moved END_STREAM (renamed from FINAL) back to HEADERS/DATA.
-
-
- Removed HEADERS+PRIORITY, added optional priority to HEADERS frame.
-
-
- Added PRIORITY frame.
-
-
-
-
-
- Added continuations to frames carrying header blocks.
-
-
- Replaced use of "session" with "connection" to avoid confusion with other HTTP stateful
- concepts, like cookies.
-
-
- Removed "message".
-
-
- Switched to TLS ALPN from NPN.
-
-
- Editorial changes.
-
-
-
-
-
- Added IANA considerations section for frame types, error codes and settings.
-
-
- Removed data frame compression.
-
-
- Added PUSH_PROMISE.
-
-
- Added globally applicable flags to framing.
-
-
- Removed zlib-based header compression mechanism.
-
-
- Updated references.
-
-
- Clarified stream identifier reuse.
-
-
- Removed CREDENTIALS frame and associated mechanisms.
-
-
- Added advice against naive implementation of flow control.
-
-
- Added session header section.
-
-
- Restructured frame header. Removed distinction between data and control frames.
-
-
- Altered flow control properties to include session-level limits.
-
-
- Added note on cacheability of pushed resources and multiple tenant servers.
-
-
- Changed protocol label form based on discussions.
-
-
-
-
-
- Changed title throughout.
-
-
- Removed section on Incompatibilities with SPDY draft#2.
-
-
- Changed INTERNAL_ERROR on GOAWAY to have a value of 2 .
-
-
- Replaced abstract and introduction.
-
-
- Added section on starting HTTP/2.0, including upgrade mechanism.
-
-
- Removed unused references.
-
-
- Added flow control principles based on .
-
-
-
-
-
- Adopted as base for draft-ietf-httpbis-http2.
-
-
- Updated authors/editors list.
-
-
- Added status note.
-
-
-
-
-
-
-
diff --git a/Godeps/_workspace/src/golang.org/x/net/http2/transport.go b/Godeps/_workspace/src/golang.org/x/net/http2/transport.go
index bbcf82a0407..cea92f2a5f2 100644
--- a/Godeps/_workspace/src/golang.org/x/net/http2/transport.go
+++ b/Godeps/_workspace/src/golang.org/x/net/http2/transport.go
@@ -187,8 +187,8 @@ type clientStream struct {
done chan struct{} // closed when stream remove from cc.streams map; close calls guarded by cc.mu
// owned by clientConnReadLoop:
- pastHeaders bool // got HEADERS w/ END_HEADERS
- pastTrailers bool // got second HEADERS frame w/ END_HEADERS
+ pastHeaders bool // got first MetaHeadersFrame (actual headers)
+ pastTrailers bool // got optional second MetaHeadersFrame (trailers)
trailer http.Header // accumulated trailers
resTrailer *http.Header // client's Response.Trailer
@@ -333,8 +333,12 @@ func (t *Transport) newTLSConfig(host string) *tls.Config {
if t.TLSClientConfig != nil {
*cfg = *t.TLSClientConfig
}
- cfg.NextProtos = []string{NextProtoTLS} // TODO: don't override if already in list
- cfg.ServerName = host
+ if !strSliceContains(cfg.NextProtos, NextProtoTLS) {
+ cfg.NextProtos = append([]string{NextProtoTLS}, cfg.NextProtos...)
+ }
+ if cfg.ServerName == "" {
+ cfg.ServerName = host
+ }
return cfg
}
@@ -401,14 +405,13 @@ func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) {
cc.bw = bufio.NewWriter(stickyErrWriter{c, &cc.werr})
cc.br = bufio.NewReader(c)
cc.fr = NewFramer(cc.bw, cc.br)
+ cc.fr.ReadMetaHeaders = hpack.NewDecoder(initialHeaderTableSize, nil)
+ cc.fr.MaxHeaderListSize = t.maxHeaderListSize()
// TODO: SetMaxDynamicTableSize, SetMaxDynamicTableSizeLimit on
// henc in response to SETTINGS frames?
cc.henc = hpack.NewEncoder(&cc.hbuf)
- type connectionStater interface {
- ConnectionState() tls.ConnectionState
- }
if cs, ok := c.(connectionStater); ok {
state := cs.ConnectionState()
cc.tlsState = &state
@@ -565,7 +568,27 @@ func (cc *ClientConn) responseHeaderTimeout() time.Duration {
return 0
}
+// checkConnHeaders checks whether req has any invalid connection-level headers.
+// per RFC 7540 section 8.1.2.2: Connection-Specific Header Fields.
+// Certain headers are special-cased as okay but not transmitted later.
+func checkConnHeaders(req *http.Request) error {
+ if v := req.Header.Get("Upgrade"); v != "" {
+ return errors.New("http2: invalid Upgrade request header")
+ }
+ if v := req.Header.Get("Transfer-Encoding"); (v != "" && v != "chunked") || len(req.Header["Transfer-Encoding"]) > 1 {
+ return errors.New("http2: invalid Transfer-Encoding request header")
+ }
+ if v := req.Header.Get("Connection"); (v != "" && v != "close" && v != "keep-alive") || len(req.Header["Connection"]) > 1 {
+ return errors.New("http2: invalid Connection request header")
+ }
+ return nil
+}
+
func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
+ if err := checkConnHeaders(req); err != nil {
+ return nil, err
+ }
+
trailers, err := commaSeparatedTrailers(req)
if err != nil {
return nil, err
@@ -917,13 +940,24 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
var didUA bool
for k, vv := range req.Header {
lowKey := strings.ToLower(k)
- if lowKey == "host" || lowKey == "content-length" {
+ switch lowKey {
+ case "host", "content-length":
+ // Host is :authority, already sent.
+ // Content-Length is automatic, set below.
continue
- }
- if lowKey == "user-agent" {
+ case "connection", "proxy-connection", "transfer-encoding", "upgrade":
+ // Per 8.1.2.2 Connection-Specific Header
+ // Fields, don't send connection-specific
+ // fields. We deal with these earlier in
+ // RoundTrip, deciding whether they're
+ // error-worthy, but we don't want to mutate
+ // the user's *Request so at this point, just
+ // skip over them at this point.
+ continue
+ case "user-agent":
// Match Go's http1 behavior: at most one
- // User-Agent. If set to nil or empty string,
- // then omit it. Otherwise if not mentioned,
+ // User-Agent. If set to nil or empty string,
+ // then omit it. Otherwise if not mentioned,
// include the default (below).
didUA = true
if len(vv) < 1 {
@@ -1033,17 +1067,9 @@ func (cc *ClientConn) streamByID(id uint32, andRemove bool) *clientStream {
// clientConnReadLoop is the state owned by the clientConn's frame-reading readLoop.
type clientConnReadLoop struct {
- cc *ClientConn
- activeRes map[uint32]*clientStream // keyed by streamID
-
- hdec *hpack.Decoder
-
- // Fields reset on each HEADERS:
- nextRes *http.Response
- sawRegHeader bool // saw non-pseudo header
- reqMalformed error // non-nil once known to be malformed
- lastHeaderEndsStream bool
- headerListSize int64 // actually uint32, but easier math this way
+ cc *ClientConn
+ activeRes map[uint32]*clientStream // keyed by streamID
+ closeWhenIdle bool
}
// readLoop runs in its own goroutine and reads and dispatches frames.
@@ -1052,7 +1078,6 @@ func (cc *ClientConn) readLoop() {
cc: cc,
activeRes: make(map[uint32]*clientStream),
}
- rl.hdec = hpack.NewDecoder(initialHeaderTableSize, rl.onNewHeaderField)
defer rl.cleanup()
cc.readerErr = rl.run()
@@ -1094,7 +1119,7 @@ func (rl *clientConnReadLoop) cleanup() {
func (rl *clientConnReadLoop) run() error {
cc := rl.cc
- closeWhenIdle := cc.t.disableKeepAlives()
+ rl.closeWhenIdle = cc.t.disableKeepAlives()
gotReply := false // ever saw a reply
for {
f, err := cc.fr.ReadFrame()
@@ -1102,8 +1127,10 @@ func (rl *clientConnReadLoop) run() error {
cc.vlogf("Transport readFrame error: (%T) %v", err, err)
}
if se, ok := err.(StreamError); ok {
- // TODO: deal with stream errors from the framer.
- return se
+ if cs := cc.streamByID(se.StreamID, true /*ended; remove it*/); cs != nil {
+ rl.endStreamError(cs, cc.fr.errDetail)
+ }
+ continue
} else if err != nil {
return err
}
@@ -1113,13 +1140,10 @@ func (rl *clientConnReadLoop) run() error {
maybeIdle := false // whether frame might transition us to idle
switch f := f.(type) {
- case *HeadersFrame:
+ case *MetaHeadersFrame:
err = rl.processHeaders(f)
maybeIdle = true
gotReply = true
- case *ContinuationFrame:
- err = rl.processContinuation(f)
- maybeIdle = true
case *DataFrame:
err = rl.processData(f)
maybeIdle = true
@@ -1143,97 +1167,102 @@ func (rl *clientConnReadLoop) run() error {
if err != nil {
return err
}
- if closeWhenIdle && gotReply && maybeIdle && len(rl.activeRes) == 0 {
+ if rl.closeWhenIdle && gotReply && maybeIdle && len(rl.activeRes) == 0 {
cc.closeIfIdle()
}
}
}
-func (rl *clientConnReadLoop) processHeaders(f *HeadersFrame) error {
- rl.sawRegHeader = false
- rl.reqMalformed = nil
- rl.lastHeaderEndsStream = f.StreamEnded()
- rl.headerListSize = 0
- rl.nextRes = &http.Response{
- Proto: "HTTP/2.0",
- ProtoMajor: 2,
- Header: make(http.Header),
- }
- rl.hdec.SetEmitEnabled(true)
- return rl.processHeaderBlockFragment(f.HeaderBlockFragment(), f.StreamID, f.HeadersEnded())
-}
-
-func (rl *clientConnReadLoop) processContinuation(f *ContinuationFrame) error {
- return rl.processHeaderBlockFragment(f.HeaderBlockFragment(), f.StreamID, f.HeadersEnded())
-}
-
-func (rl *clientConnReadLoop) processHeaderBlockFragment(frag []byte, streamID uint32, finalFrag bool) error {
+func (rl *clientConnReadLoop) processHeaders(f *MetaHeadersFrame) error {
cc := rl.cc
- streamEnded := rl.lastHeaderEndsStream
- cs := cc.streamByID(streamID, streamEnded && finalFrag)
+ cs := cc.streamByID(f.StreamID, f.StreamEnded())
if cs == nil {
// We'd get here if we canceled a request while the
- // server was mid-way through replying with its
- // headers. (The case of a CONTINUATION arriving
- // without HEADERS would be rejected earlier by the
- // Framer). So if this was just something we canceled,
- // ignore it.
+ // server had its response still in flight. So if this
+ // was just something we canceled, ignore it.
return nil
}
- if cs.pastHeaders {
- rl.hdec.SetEmitFunc(func(f hpack.HeaderField) { rl.onNewTrailerField(cs, f) })
+ if !cs.pastHeaders {
+ cs.pastHeaders = true
} else {
- rl.hdec.SetEmitFunc(rl.onNewHeaderField)
+ return rl.processTrailers(cs, f)
}
- _, err := rl.hdec.Write(frag)
+
+ res, err := rl.handleResponse(cs, f)
if err != nil {
- return ConnectionError(ErrCodeCompression)
- }
- if finalFrag {
- if err := rl.hdec.Close(); err != nil {
- return ConnectionError(ErrCodeCompression)
+ if _, ok := err.(ConnectionError); ok {
+ return err
}
+ // Any other error type is a stream error.
+ cs.cc.writeStreamReset(f.StreamID, ErrCodeProtocol, err)
+ cs.resc <- resAndError{err: err}
+ return nil // return nil from process* funcs to keep conn alive
}
-
- if !finalFrag {
+ if res == nil {
+ // (nil, nil) special case. See handleResponse docs.
return nil
}
-
- if !cs.pastHeaders {
- cs.pastHeaders = true
- } else {
- // We're dealing with trailers. (and specifically the
- // final frame of headers)
- if cs.pastTrailers {
- // Too many HEADERS frames for this stream.
- return ConnectionError(ErrCodeProtocol)
- }
- cs.pastTrailers = true
- if !streamEnded {
- // We expect that any header block fragment
- // frame for trailers with END_HEADERS also
- // has END_STREAM.
- return ConnectionError(ErrCodeProtocol)
- }
- rl.endStream(cs)
- return nil
+ if res.Body != noBody {
+ rl.activeRes[cs.ID] = cs
}
+ cs.resTrailer = &res.Trailer
+ cs.resc <- resAndError{res: res}
+ return nil
+}
- if rl.reqMalformed != nil {
- cs.resc <- resAndError{err: rl.reqMalformed}
- rl.cc.writeStreamReset(cs.ID, ErrCodeProtocol, rl.reqMalformed)
- return nil
+// may return error types nil, or ConnectionError. Any other error value
+// is a StreamError of type ErrCodeProtocol. The returned error in that case
+// is the detail.
+//
+// As a special case, handleResponse may return (nil, nil) to skip the
+// frame (currently only used for 100 expect continue). This special
+// case is going away after Issue 13851 is fixed.
+func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFrame) (*http.Response, error) {
+ if f.Truncated {
+ return nil, errResponseHeaderListSize
}
- res := rl.nextRes
+ status := f.PseudoValue("status")
+ if status == "" {
+ return nil, errors.New("missing status pseudo header")
+ }
+ statusCode, err := strconv.Atoi(status)
+ if err != nil {
+ return nil, errors.New("malformed non-numeric status pseudo header")
+ }
- if res.StatusCode == 100 {
+ if statusCode == 100 {
// Just skip 100-continue response headers for now.
// TODO: golang.org/issue/13851 for doing it properly.
cs.pastHeaders = false // do it all again
- return nil
+ return nil, nil
+ }
+
+ header := make(http.Header)
+ res := &http.Response{
+ Proto: "HTTP/2.0",
+ ProtoMajor: 2,
+ Header: header,
+ StatusCode: statusCode,
+ Status: status + " " + http.StatusText(statusCode),
+ }
+ for _, hf := range f.RegularFields() {
+ key := http.CanonicalHeaderKey(hf.Name)
+ if key == "Trailer" {
+ t := res.Trailer
+ if t == nil {
+ t = make(http.Header)
+ res.Trailer = t
+ }
+ foreachHeaderElement(hf.Value, func(v string) {
+ t[http.CanonicalHeaderKey(v)] = nil
+ })
+ } else {
+ header[key] = append(header[key], hf.Value)
+ }
}
+ streamEnded := f.StreamEnded()
if !streamEnded || cs.req.Method == "HEAD" {
res.ContentLength = -1
if clens := res.Header["Content-Length"]; len(clens) == 1 {
@@ -1251,25 +1280,49 @@ func (rl *clientConnReadLoop) processHeaderBlockFragment(frag []byte, streamID u
if streamEnded {
res.Body = noBody
- } else {
- buf := new(bytes.Buffer) // TODO(bradfitz): recycle this garbage
- cs.bufPipe = pipe{b: buf}
- cs.bytesRemain = res.ContentLength
- res.Body = transportResponseBody{cs}
- go cs.awaitRequestCancel(requestCancel(cs.req))
-
- if cs.requestedGzip && res.Header.Get("Content-Encoding") == "gzip" {
- res.Header.Del("Content-Encoding")
- res.Header.Del("Content-Length")
- res.ContentLength = -1
- res.Body = &gzipReader{body: res.Body}
- }
+ return res, nil
}
- cs.resTrailer = &res.Trailer
- rl.activeRes[cs.ID] = cs
- cs.resc <- resAndError{res: res}
- rl.nextRes = nil // unused now; will be reset next HEADERS frame
+ buf := new(bytes.Buffer) // TODO(bradfitz): recycle this garbage
+ cs.bufPipe = pipe{b: buf}
+ cs.bytesRemain = res.ContentLength
+ res.Body = transportResponseBody{cs}
+ go cs.awaitRequestCancel(requestCancel(cs.req))
+
+ if cs.requestedGzip && res.Header.Get("Content-Encoding") == "gzip" {
+ res.Header.Del("Content-Encoding")
+ res.Header.Del("Content-Length")
+ res.ContentLength = -1
+ res.Body = &gzipReader{body: res.Body}
+ }
+ return res, nil
+}
+
+func (rl *clientConnReadLoop) processTrailers(cs *clientStream, f *MetaHeadersFrame) error {
+ if cs.pastTrailers {
+ // Too many HEADERS frames for this stream.
+ return ConnectionError(ErrCodeProtocol)
+ }
+ cs.pastTrailers = true
+ if !f.StreamEnded() {
+ // We expect that any headers for trailers also
+ // has END_STREAM.
+ return ConnectionError(ErrCodeProtocol)
+ }
+ if len(f.PseudoFields()) > 0 {
+ // No pseudo header fields are defined for trailers.
+ // TODO: ConnectionError might be overly harsh? Check.
+ return ConnectionError(ErrCodeProtocol)
+ }
+
+ trailer := make(http.Header)
+ for _, hf := range f.RegularFields() {
+ key := http.CanonicalHeaderKey(hf.Name)
+ trailer[key] = append(trailer[key], hf.Value)
+ }
+ cs.trailer = trailer
+
+ rl.endStream(cs)
return nil
}
@@ -1387,6 +1440,7 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error {
cc.mu.Unlock()
if _, err := cs.bufPipe.Write(data); err != nil {
+ rl.endStreamError(cs, err)
return err
}
}
@@ -1402,14 +1456,20 @@ var errInvalidTrailers = errors.New("http2: invalid trailers")
func (rl *clientConnReadLoop) endStream(cs *clientStream) {
// TODO: check that any declared content-length matches, like
// server.go's (*stream).endStream method.
- err := io.EOF
- code := cs.copyTrailers
- if rl.reqMalformed != nil {
- err = rl.reqMalformed
- code = nil
+ rl.endStreamError(cs, nil)
+}
+
+func (rl *clientConnReadLoop) endStreamError(cs *clientStream, err error) {
+ var code func()
+ if err == nil {
+ err = io.EOF
+ code = cs.copyTrailers
}
cs.bufPipe.closeWithErrorAndCode(err, code)
delete(rl.activeRes, cs.ID)
+ if cs.req.Close || cs.req.Header.Get("Connection") == "close" {
+ rl.closeWhenIdle = true
+ }
}
func (cs *clientStream) copyTrailers() {
@@ -1542,118 +1602,6 @@ var (
errPseudoTrailers = errors.New("http2: invalid pseudo header in trailers")
)
-func (rl *clientConnReadLoop) checkHeaderField(f hpack.HeaderField) bool {
- if rl.reqMalformed != nil {
- return false
- }
-
- const headerFieldOverhead = 32 // per spec
- rl.headerListSize += int64(len(f.Name)) + int64(len(f.Value)) + headerFieldOverhead
- if max := rl.cc.t.maxHeaderListSize(); max != 0 && rl.headerListSize > int64(max) {
- rl.hdec.SetEmitEnabled(false)
- rl.reqMalformed = errResponseHeaderListSize
- return false
- }
-
- if !validHeaderFieldValue(f.Value) {
- rl.reqMalformed = errInvalidHeaderFieldValue
- return false
- }
-
- isPseudo := strings.HasPrefix(f.Name, ":")
- if isPseudo {
- if rl.sawRegHeader {
- rl.reqMalformed = errors.New("http2: invalid pseudo header after regular header")
- return false
- }
- } else {
- if !validHeaderFieldName(f.Name) {
- rl.reqMalformed = errInvalidHeaderFieldName
- return false
- }
- rl.sawRegHeader = true
- }
-
- return true
-}
-
-// onNewHeaderField runs on the readLoop goroutine whenever a new
-// hpack header field is decoded.
-func (rl *clientConnReadLoop) onNewHeaderField(f hpack.HeaderField) {
- cc := rl.cc
- if VerboseLogs {
- cc.logf("http2: Transport decoded %v", f)
- }
-
- if !rl.checkHeaderField(f) {
- return
- }
-
- isPseudo := strings.HasPrefix(f.Name, ":")
- if isPseudo {
- switch f.Name {
- case ":status":
- code, err := strconv.Atoi(f.Value)
- if err != nil {
- rl.reqMalformed = errors.New("http2: invalid :status")
- return
- }
- rl.nextRes.Status = f.Value + " " + http.StatusText(code)
- rl.nextRes.StatusCode = code
- default:
- // "Endpoints MUST NOT generate pseudo-header
- // fields other than those defined in this
- // document."
- rl.reqMalformed = fmt.Errorf("http2: unknown response pseudo header %q", f.Name)
- }
- return
- }
-
- key := http.CanonicalHeaderKey(f.Name)
- if key == "Trailer" {
- t := rl.nextRes.Trailer
- if t == nil {
- t = make(http.Header)
- rl.nextRes.Trailer = t
- }
- foreachHeaderElement(f.Value, func(v string) {
- t[http.CanonicalHeaderKey(v)] = nil
- })
- } else {
- rl.nextRes.Header.Add(key, f.Value)
- }
-}
-
-func (rl *clientConnReadLoop) onNewTrailerField(cs *clientStream, f hpack.HeaderField) {
- if VerboseLogs {
- rl.cc.logf("http2: Transport decoded trailer %v", f)
- }
- if !rl.checkHeaderField(f) {
- return
- }
- if strings.HasPrefix(f.Name, ":") {
- // Pseudo-header fields MUST NOT appear in
- // trailers. Endpoints MUST treat a request or
- // response that contains undefined or invalid
- // pseudo-header fields as malformed.
- rl.reqMalformed = errPseudoTrailers
- return
- }
-
- key := http.CanonicalHeaderKey(f.Name)
-
- // The spec says one must predeclare their trailers but in practice
- // popular users (which is to say the only user we found) do not so we
- // violate the spec and accept all of them.
- const acceptAllTrailers = true
- if _, ok := (*cs.resTrailer)[key]; ok || acceptAllTrailers {
- if cs.trailer == nil {
- cs.trailer = make(http.Header)
- }
- cs.trailer[key] = append(cs.trailer[key], f.Value)
- }
-}
-
func (cc *ClientConn) logf(format string, args ...interface{}) {
cc.t.logf(format, args...)
}
@@ -1691,13 +1639,18 @@ func (rt erringRoundTripper) RoundTrip(*http.Request) (*http.Response, error) {
// call gzip.NewReader on the first call to Read
type gzipReader struct {
body io.ReadCloser // underlying Response.Body
- zr io.Reader // lazily-initialized gzip reader
+ zr *gzip.Reader // lazily-initialized gzip reader
+ zerr error // sticky error
}
func (gz *gzipReader) Read(p []byte) (n int, err error) {
+ if gz.zerr != nil {
+ return 0, gz.zerr
+ }
if gz.zr == nil {
gz.zr, err = gzip.NewReader(gz.body)
if err != nil {
+ gz.zerr = err
return 0, err
}
}
diff --git a/Godeps/_workspace/src/golang.org/x/net/http2/transport_test.go b/Godeps/_workspace/src/golang.org/x/net/http2/transport_test.go
deleted file mode 100644
index 4e3accc1218..00000000000
--- a/Godeps/_workspace/src/golang.org/x/net/http2/transport_test.go
+++ /dev/null
@@ -1,1551 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package http2
-
-import (
- "bufio"
- "bytes"
- "crypto/tls"
- "errors"
- "flag"
- "fmt"
- "io"
- "io/ioutil"
- "log"
- "math/rand"
- "net"
- "net/http"
- "net/url"
- "os"
- "reflect"
- "strconv"
- "strings"
- "sync"
- "sync/atomic"
- "testing"
- "time"
-
- "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/http2/hpack"
-)
-
-var (
- extNet = flag.Bool("extnet", false, "do external network tests")
- transportHost = flag.String("transporthost", "http2.golang.org", "hostname to use for TestTransport")
- insecure = flag.Bool("insecure", false, "insecure TLS dials") // TODO: dead code. remove?
-)
-
-var tlsConfigInsecure = &tls.Config{InsecureSkipVerify: true}
-
-func TestTransportExternal(t *testing.T) {
- if !*extNet {
- t.Skip("skipping external network test")
- }
- req, _ := http.NewRequest("GET", "https://"+*transportHost+"/", nil)
- rt := &Transport{TLSClientConfig: tlsConfigInsecure}
- res, err := rt.RoundTrip(req)
- if err != nil {
- t.Fatalf("%v", err)
- }
- res.Write(os.Stdout)
-}
-
-func TestTransport(t *testing.T) {
- const body = "sup"
- st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
- io.WriteString(w, body)
- }, optOnlyServer)
- defer st.Close()
-
- tr := &Transport{TLSClientConfig: tlsConfigInsecure}
- defer tr.CloseIdleConnections()
-
- req, err := http.NewRequest("GET", st.ts.URL, nil)
- if err != nil {
- t.Fatal(err)
- }
- res, err := tr.RoundTrip(req)
- if err != nil {
- t.Fatal(err)
- }
- defer res.Body.Close()
-
- t.Logf("Got res: %+v", res)
- if g, w := res.StatusCode, 200; g != w {
- t.Errorf("StatusCode = %v; want %v", g, w)
- }
- if g, w := res.Status, "200 OK"; g != w {
- t.Errorf("Status = %q; want %q", g, w)
- }
- wantHeader := http.Header{
- "Content-Length": []string{"3"},
- "Content-Type": []string{"text/plain; charset=utf-8"},
- "Date": []string{"XXX"}, // see cleanDate
- }
- cleanDate(res)
- if !reflect.DeepEqual(res.Header, wantHeader) {
- t.Errorf("res Header = %v; want %v", res.Header, wantHeader)
- }
- if res.Request != req {
- t.Errorf("Response.Request = %p; want %p", res.Request, req)
- }
- if res.TLS == nil {
- t.Error("Response.TLS = nil; want non-nil")
- }
- slurp, err := ioutil.ReadAll(res.Body)
- if err != nil {
- t.Errorf("Body read: %v", err)
- } else if string(slurp) != body {
- t.Errorf("Body = %q; want %q", slurp, body)
- }
-}
-
-func TestTransportReusesConns(t *testing.T) {
- st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
- io.WriteString(w, r.RemoteAddr)
- }, optOnlyServer)
- defer st.Close()
- tr := &Transport{TLSClientConfig: tlsConfigInsecure}
- defer tr.CloseIdleConnections()
- get := func() string {
- req, err := http.NewRequest("GET", st.ts.URL, nil)
- if err != nil {
- t.Fatal(err)
- }
- res, err := tr.RoundTrip(req)
- if err != nil {
- t.Fatal(err)
- }
- defer res.Body.Close()
- slurp, err := ioutil.ReadAll(res.Body)
- if err != nil {
- t.Fatalf("Body read: %v", err)
- }
- addr := strings.TrimSpace(string(slurp))
- if addr == "" {
- t.Fatalf("didn't get an addr in response")
- }
- return addr
- }
- first := get()
- second := get()
- if first != second {
- t.Errorf("first and second responses were on different connections: %q vs %q", first, second)
- }
-}
-
-// Tests that the Transport only keeps one pending dial open per destination address.
-// https://golang.org/issue/13397
-func TestTransportGroupsPendingDials(t *testing.T) {
- st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
- io.WriteString(w, r.RemoteAddr)
- }, optOnlyServer)
- defer st.Close()
- tr := &Transport{
- TLSClientConfig: tlsConfigInsecure,
- }
- defer tr.CloseIdleConnections()
- var (
- mu sync.Mutex
- dials = map[string]int{}
- )
- var wg sync.WaitGroup
- for i := 0; i < 10; i++ {
- wg.Add(1)
- go func() {
- defer wg.Done()
- req, err := http.NewRequest("GET", st.ts.URL, nil)
- if err != nil {
- t.Error(err)
- return
- }
- res, err := tr.RoundTrip(req)
- if err != nil {
- t.Error(err)
- return
- }
- defer res.Body.Close()
- slurp, err := ioutil.ReadAll(res.Body)
- if err != nil {
- t.Errorf("Body read: %v", err)
- }
- addr := strings.TrimSpace(string(slurp))
- if addr == "" {
- t.Errorf("didn't get an addr in response")
- }
- mu.Lock()
- dials[addr]++
- mu.Unlock()
- }()
- }
- wg.Wait()
- if len(dials) != 1 {
- t.Errorf("saw %d dials; want 1: %v", len(dials), dials)
- }
- tr.CloseIdleConnections()
- if err := retry(50, 10*time.Millisecond, func() error {
- cp, ok := tr.connPool().(*clientConnPool)
- if !ok {
- return fmt.Errorf("Conn pool is %T; want *clientConnPool", tr.connPool())
- }
- cp.mu.Lock()
- defer cp.mu.Unlock()
- if len(cp.dialing) != 0 {
- return fmt.Errorf("dialing map = %v; want empty", cp.dialing)
- }
- if len(cp.conns) != 0 {
- return fmt.Errorf("conns = %v; want empty", cp.conns)
- }
- if len(cp.keys) != 0 {
- return fmt.Errorf("keys = %v; want empty", cp.keys)
- }
- return nil
- }); err != nil {
- t.Errorf("State of pool after CloseIdleConnections: %v", err)
- }
-}
-
-func retry(tries int, delay time.Duration, fn func() error) error {
- var err error
- for i := 0; i < tries; i++ {
- err = fn()
- if err == nil {
- return nil
- }
- time.Sleep(delay)
- }
- return err
-}
-
-func TestTransportAbortClosesPipes(t *testing.T) {
- shutdown := make(chan struct{})
- st := newServerTester(t,
- func(w http.ResponseWriter, r *http.Request) {
- w.(http.Flusher).Flush()
- <-shutdown
- },
- optOnlyServer,
- )
- defer st.Close()
- defer close(shutdown) // we must shutdown before st.Close() to avoid hanging
-
- done := make(chan struct{})
- requestMade := make(chan struct{})
- go func() {
- defer close(done)
- tr := &Transport{TLSClientConfig: tlsConfigInsecure}
- req, err := http.NewRequest("GET", st.ts.URL, nil)
- if err != nil {
- t.Fatal(err)
- }
- res, err := tr.RoundTrip(req)
- if err != nil {
- t.Fatal(err)
- }
- defer res.Body.Close()
- close(requestMade)
- _, err = ioutil.ReadAll(res.Body)
- if err == nil {
- t.Error("expected error from res.Body.Read")
- }
- }()
-
- <-requestMade
- // Now force the serve loop to end, via closing the connection.
- st.closeConn()
- // deadlock? that's a bug.
- select {
- case <-done:
- case <-time.After(3 * time.Second):
- t.Fatal("timeout")
- }
-}
-
-// TODO: merge this with TestTransportBody to make TestTransportRequest? This
-// could be a table-driven test with extra goodies.
-func TestTransportPath(t *testing.T) {
- gotc := make(chan *url.URL, 1)
- st := newServerTester(t,
- func(w http.ResponseWriter, r *http.Request) {
- gotc <- r.URL
- },
- optOnlyServer,
- )
- defer st.Close()
-
- tr := &Transport{TLSClientConfig: tlsConfigInsecure}
- defer tr.CloseIdleConnections()
- const (
- path = "/testpath"
- query = "q=1"
- )
- surl := st.ts.URL + path + "?" + query
- req, err := http.NewRequest("POST", surl, nil)
- if err != nil {
- t.Fatal(err)
- }
- c := &http.Client{Transport: tr}
- res, err := c.Do(req)
- if err != nil {
- t.Fatal(err)
- }
- defer res.Body.Close()
- got := <-gotc
- if got.Path != path {
- t.Errorf("Read Path = %q; want %q", got.Path, path)
- }
- if got.RawQuery != query {
- t.Errorf("Read RawQuery = %q; want %q", got.RawQuery, query)
- }
-}
-
-func randString(n int) string {
- rnd := rand.New(rand.NewSource(int64(n)))
- b := make([]byte, n)
- for i := range b {
- b[i] = byte(rnd.Intn(256))
- }
- return string(b)
-}
-
-var bodyTests = []struct {
- body string
- noContentLen bool
-}{
- {body: "some message"},
- {body: "some message", noContentLen: true},
- {body: ""},
- {body: "", noContentLen: true},
- {body: strings.Repeat("a", 1<<20), noContentLen: true},
- {body: strings.Repeat("a", 1<<20)},
- {body: randString(16<<10 - 1)},
- {body: randString(16 << 10)},
- {body: randString(16<<10 + 1)},
- {body: randString(512<<10 - 1)},
- {body: randString(512 << 10)},
- {body: randString(512<<10 + 1)},
- {body: randString(1<<20 - 1)},
- {body: randString(1 << 20)},
- {body: randString(1<<20 + 2)},
-}
-
-func TestTransportBody(t *testing.T) {
- type reqInfo struct {
- req *http.Request
- slurp []byte
- err error
- }
- gotc := make(chan reqInfo, 1)
- st := newServerTester(t,
- func(w http.ResponseWriter, r *http.Request) {
- slurp, err := ioutil.ReadAll(r.Body)
- if err != nil {
- gotc <- reqInfo{err: err}
- } else {
- gotc <- reqInfo{req: r, slurp: slurp}
- }
- },
- optOnlyServer,
- )
- defer st.Close()
-
- for i, tt := range bodyTests {
- tr := &Transport{TLSClientConfig: tlsConfigInsecure}
- defer tr.CloseIdleConnections()
-
- var body io.Reader = strings.NewReader(tt.body)
- if tt.noContentLen {
- body = struct{ io.Reader }{body} // just a Reader, hiding concrete type and other methods
- }
- req, err := http.NewRequest("POST", st.ts.URL, body)
- if err != nil {
- t.Fatalf("#%d: %v", i, err)
- }
- c := &http.Client{Transport: tr}
- res, err := c.Do(req)
- if err != nil {
- t.Fatalf("#%d: %v", i, err)
- }
- defer res.Body.Close()
- ri := <-gotc
- if ri.err != nil {
- t.Errorf("%#d: read error: %v", i, ri.err)
- continue
- }
- if got := string(ri.slurp); got != tt.body {
- t.Errorf("#%d: Read body mismatch.\n got: %q (len %d)\nwant: %q (len %d)", i, shortString(got), len(got), shortString(tt.body), len(tt.body))
- }
- wantLen := int64(len(tt.body))
- if tt.noContentLen && tt.body != "" {
- wantLen = -1
- }
- if ri.req.ContentLength != wantLen {
- t.Errorf("#%d. handler got ContentLength = %v; want %v", i, ri.req.ContentLength, wantLen)
- }
- }
-}
-
-func shortString(v string) string {
- const maxLen = 100
- if len(v) <= maxLen {
- return v
- }
- return fmt.Sprintf("%v[...%d bytes omitted...]%v", v[:maxLen/2], len(v)-maxLen, v[len(v)-maxLen/2:])
-}
-
-func TestTransportDialTLS(t *testing.T) {
- var mu sync.Mutex // guards following
- var gotReq, didDial bool
-
- ts := newServerTester(t,
- func(w http.ResponseWriter, r *http.Request) {
- mu.Lock()
- gotReq = true
- mu.Unlock()
- },
- optOnlyServer,
- )
- defer ts.Close()
- tr := &Transport{
- DialTLS: func(netw, addr string, cfg *tls.Config) (net.Conn, error) {
- mu.Lock()
- didDial = true
- mu.Unlock()
- cfg.InsecureSkipVerify = true
- c, err := tls.Dial(netw, addr, cfg)
- if err != nil {
- return nil, err
- }
- return c, c.Handshake()
- },
- }
- defer tr.CloseIdleConnections()
- client := &http.Client{Transport: tr}
- res, err := client.Get(ts.ts.URL)
- if err != nil {
- t.Fatal(err)
- }
- res.Body.Close()
- mu.Lock()
- if !gotReq {
- t.Error("didn't get request")
- }
- if !didDial {
- t.Error("didn't use dial hook")
- }
-}
-
-func TestConfigureTransport(t *testing.T) {
- t1 := &http.Transport{}
- err := ConfigureTransport(t1)
- if err == errTransportVersion {
- t.Skip(err)
- }
- if err != nil {
- t.Fatal(err)
- }
- if got := fmt.Sprintf("%#v", *t1); !strings.Contains(got, `"h2"`) {
- // Laziness, to avoid buildtags.
- t.Errorf("stringification of HTTP/1 transport didn't contain \"h2\": %v", got)
- }
- wantNextProtos := []string{"h2", "http/1.1"}
- if t1.TLSClientConfig == nil {
- t.Errorf("nil t1.TLSClientConfig")
- } else if !reflect.DeepEqual(t1.TLSClientConfig.NextProtos, wantNextProtos) {
- t.Errorf("TLSClientConfig.NextProtos = %q; want %q", t1.TLSClientConfig.NextProtos, wantNextProtos)
- }
- if err := ConfigureTransport(t1); err == nil {
- t.Error("unexpected success on second call to ConfigureTransport")
- }
-
- // And does it work?
- st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
- io.WriteString(w, r.Proto)
- }, optOnlyServer)
- defer st.Close()
-
- t1.TLSClientConfig.InsecureSkipVerify = true
- c := &http.Client{Transport: t1}
- res, err := c.Get(st.ts.URL)
- if err != nil {
- t.Fatal(err)
- }
- slurp, err := ioutil.ReadAll(res.Body)
- if err != nil {
- t.Fatal(err)
- }
- if got, want := string(slurp), "HTTP/2.0"; got != want {
- t.Errorf("body = %q; want %q", got, want)
- }
-}
-
-type capitalizeReader struct {
- r io.Reader
-}
-
-func (cr capitalizeReader) Read(p []byte) (n int, err error) {
- n, err = cr.r.Read(p)
- for i, b := range p[:n] {
- if b >= 'a' && b <= 'z' {
- p[i] = b - ('a' - 'A')
- }
- }
- return
-}
-
-type flushWriter struct {
- w io.Writer
-}
-
-func (fw flushWriter) Write(p []byte) (n int, err error) {
- n, err = fw.w.Write(p)
- if f, ok := fw.w.(http.Flusher); ok {
- f.Flush()
- }
- return
-}
-
-type clientTester struct {
- t *testing.T
- tr *Transport
- sc, cc net.Conn // server and client conn
- fr *Framer // server's framer
- client func() error
- server func() error
-}
-
-func newClientTester(t *testing.T) *clientTester {
- var dialOnce struct {
- sync.Mutex
- dialed bool
- }
- ct := &clientTester{
- t: t,
- }
- ct.tr = &Transport{
- TLSClientConfig: tlsConfigInsecure,
- DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
- dialOnce.Lock()
- defer dialOnce.Unlock()
- if dialOnce.dialed {
- return nil, errors.New("only one dial allowed in test mode")
- }
- dialOnce.dialed = true
- return ct.cc, nil
- },
- }
-
- ln := newLocalListener(t)
- cc, err := net.Dial("tcp", ln.Addr().String())
- if err != nil {
- t.Fatal(err)
-
- }
- sc, err := ln.Accept()
- if err != nil {
- t.Fatal(err)
- }
- ln.Close()
- ct.cc = cc
- ct.sc = sc
- ct.fr = NewFramer(sc, sc)
- return ct
-}
-
-func newLocalListener(t *testing.T) net.Listener {
- ln, err := net.Listen("tcp4", "127.0.0.1:0")
- if err == nil {
- return ln
- }
- ln, err = net.Listen("tcp6", "[::1]:0")
- if err != nil {
- t.Fatal(err)
- }
- return ln
-}
-
-func (ct *clientTester) greet() {
- buf := make([]byte, len(ClientPreface))
- _, err := io.ReadFull(ct.sc, buf)
- if err != nil {
- ct.t.Fatalf("reading client preface: %v", err)
- }
- f, err := ct.fr.ReadFrame()
- if err != nil {
- ct.t.Fatalf("Reading client settings frame: %v", err)
- }
- if sf, ok := f.(*SettingsFrame); !ok {
- ct.t.Fatalf("Wanted client settings frame; got %v", f)
- _ = sf // stash it away?
- }
- if err := ct.fr.WriteSettings(); err != nil {
- ct.t.Fatal(err)
- }
- if err := ct.fr.WriteSettingsAck(); err != nil {
- ct.t.Fatal(err)
- }
-}
-
-func (ct *clientTester) cleanup() {
- ct.tr.CloseIdleConnections()
-}
-
-func (ct *clientTester) run() {
- errc := make(chan error, 2)
- ct.start("client", errc, ct.client)
- ct.start("server", errc, ct.server)
- defer ct.cleanup()
- for i := 0; i < 2; i++ {
- if err := <-errc; err != nil {
- ct.t.Error(err)
- return
- }
- }
-}
-
-func (ct *clientTester) start(which string, errc chan<- error, fn func() error) {
- go func() {
- finished := false
- var err error
- defer func() {
- if !finished {
- err = fmt.Errorf("%s goroutine didn't finish.", which)
- } else if err != nil {
- err = fmt.Errorf("%s: %v", which, err)
- }
- errc <- err
- }()
- err = fn()
- finished = true
- }()
-}
-
-type countingReader struct {
- n *int64
-}
-
-func (r countingReader) Read(p []byte) (n int, err error) {
- for i := range p {
- p[i] = byte(i)
- }
- atomic.AddInt64(r.n, int64(len(p)))
- return len(p), err
-}
-
-func TestTransportReqBodyAfterResponse_200(t *testing.T) { testTransportReqBodyAfterResponse(t, 200) }
-func TestTransportReqBodyAfterResponse_403(t *testing.T) { testTransportReqBodyAfterResponse(t, 403) }
-
-func testTransportReqBodyAfterResponse(t *testing.T, status int) {
- const bodySize = 10 << 20
- ct := newClientTester(t)
- ct.client = func() error {
- var n int64 // atomic
- req, err := http.NewRequest("PUT", "https://dummy.tld/", io.LimitReader(countingReader{&n}, bodySize))
- if err != nil {
- return err
- }
- res, err := ct.tr.RoundTrip(req)
- if err != nil {
- return fmt.Errorf("RoundTrip: %v", err)
- }
- defer res.Body.Close()
- if res.StatusCode != status {
- return fmt.Errorf("status code = %v; want %v", res.StatusCode, status)
- }
- slurp, err := ioutil.ReadAll(res.Body)
- if err != nil {
- return fmt.Errorf("Slurp: %v", err)
- }
- if len(slurp) > 0 {
- return fmt.Errorf("unexpected body: %q", slurp)
- }
- if status == 200 {
- if got := atomic.LoadInt64(&n); got != bodySize {
- return fmt.Errorf("For 200 response, Transport wrote %d bytes; want %d", got, bodySize)
- }
- } else {
- if got := atomic.LoadInt64(&n); got == 0 || got >= bodySize {
- return fmt.Errorf("For %d response, Transport wrote %d bytes; want (0,%d) exclusive", status, got, bodySize)
- }
- }
- return nil
- }
- ct.server = func() error {
- ct.greet()
- var buf bytes.Buffer
- enc := hpack.NewEncoder(&buf)
- var dataRecv int64
- var closed bool
- for {
- f, err := ct.fr.ReadFrame()
- if err != nil {
- return err
- }
- //println(fmt.Sprintf("server got frame: %v", f))
- switch f := f.(type) {
- case *WindowUpdateFrame, *SettingsFrame:
- case *HeadersFrame:
- if !f.HeadersEnded() {
- return fmt.Errorf("headers should have END_HEADERS be ended: %v", f)
- }
- if f.StreamEnded() {
- return fmt.Errorf("headers contains END_STREAM unexpectedly: %v", f)
- }
- time.Sleep(50 * time.Millisecond) // let client send body
- enc.WriteField(hpack.HeaderField{Name: ":status", Value: strconv.Itoa(status)})
- ct.fr.WriteHeaders(HeadersFrameParam{
- StreamID: f.StreamID,
- EndHeaders: true,
- EndStream: false,
- BlockFragment: buf.Bytes(),
- })
- case *DataFrame:
- dataLen := len(f.Data())
- dataRecv += int64(dataLen)
- if dataLen > 0 {
- if err := ct.fr.WriteWindowUpdate(0, uint32(dataLen)); err != nil {
- return err
- }
- if err := ct.fr.WriteWindowUpdate(f.StreamID, uint32(dataLen)); err != nil {
- return err
- }
- }
- if !closed && ((status != 200 && dataRecv > 0) ||
- (status == 200 && dataRecv == bodySize)) {
- closed = true
- if err := ct.fr.WriteData(f.StreamID, true, nil); err != nil {
- return err
- }
- return nil
- }
- default:
- return fmt.Errorf("Unexpected client frame %v", f)
- }
- }
- return nil
- }
- ct.run()
-}
-
-// See golang.org/issue/13444
-func TestTransportFullDuplex(t *testing.T) {
- st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
- w.WriteHeader(200) // redundant but for clarity
- w.(http.Flusher).Flush()
- io.Copy(flushWriter{w}, capitalizeReader{r.Body})
- fmt.Fprintf(w, "bye.\n")
- }, optOnlyServer)
- defer st.Close()
-
- tr := &Transport{TLSClientConfig: tlsConfigInsecure}
- defer tr.CloseIdleConnections()
- c := &http.Client{Transport: tr}
-
- pr, pw := io.Pipe()
- req, err := http.NewRequest("PUT", st.ts.URL, ioutil.NopCloser(pr))
- if err != nil {
- log.Fatal(err)
- }
- req.ContentLength = -1
- res, err := c.Do(req)
- if err != nil {
- log.Fatal(err)
- }
- defer res.Body.Close()
- if res.StatusCode != 200 {
- t.Fatalf("StatusCode = %v; want %v", res.StatusCode, 200)
- }
- bs := bufio.NewScanner(res.Body)
- want := func(v string) {
- if !bs.Scan() {
- t.Fatalf("wanted to read %q but Scan() = false, err = %v", v, bs.Err())
- }
- }
- write := func(v string) {
- _, err := io.WriteString(pw, v)
- if err != nil {
- t.Fatalf("pipe write: %v", err)
- }
- }
- write("foo\n")
- want("FOO")
- write("bar\n")
- want("BAR")
- pw.Close()
- want("bye.")
- if err := bs.Err(); err != nil {
- t.Fatal(err)
- }
-}
-
-func TestTransportConnectRequest(t *testing.T) {
- gotc := make(chan *http.Request, 1)
- st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
- gotc <- r
- }, optOnlyServer)
- defer st.Close()
-
- u, err := url.Parse(st.ts.URL)
- if err != nil {
- t.Fatal(err)
- }
-
- tr := &Transport{TLSClientConfig: tlsConfigInsecure}
- defer tr.CloseIdleConnections()
- c := &http.Client{Transport: tr}
-
- tests := []struct {
- req *http.Request
- want string
- }{
- {
- req: &http.Request{
- Method: "CONNECT",
- Header: http.Header{},
- URL: u,
- },
- want: u.Host,
- },
- {
- req: &http.Request{
- Method: "CONNECT",
- Header: http.Header{},
- URL: u,
- Host: "example.com:123",
- },
- want: "example.com:123",
- },
- }
-
- for i, tt := range tests {
- res, err := c.Do(tt.req)
- if err != nil {
- t.Errorf("%d. RoundTrip = %v", i, err)
- continue
- }
- res.Body.Close()
- req := <-gotc
- if req.Method != "CONNECT" {
- t.Errorf("method = %q; want CONNECT", req.Method)
- }
- if req.Host != tt.want {
- t.Errorf("Host = %q; want %q", req.Host, tt.want)
- }
- if req.URL.Host != tt.want {
- t.Errorf("URL.Host = %q; want %q", req.URL.Host, tt.want)
- }
- }
-}
-
-type headerType int
-
-const (
- noHeader headerType = iota // omitted
- oneHeader
- splitHeader // broken into continuation on purpose
-)
-
-const (
- f0 = noHeader
- f1 = oneHeader
- f2 = splitHeader
- d0 = false
- d1 = true
-)
-
-// Test all 36 combinations of response frame orders:
-// (3 ways of 100-continue) * (2 ways of headers) * (2 ways of data) * (3 ways of trailers):func TestTransportResponsePattern_00f0(t *testing.T) { testTransportResponsePattern(h0, h1, false, h0) }
-// Generated by http://play.golang.org/p/SScqYKJYXd
-func TestTransportResPattern_c0h1d0t0(t *testing.T) { testTransportResPattern(t, f0, f1, d0, f0) }
-func TestTransportResPattern_c0h1d0t1(t *testing.T) { testTransportResPattern(t, f0, f1, d0, f1) }
-func TestTransportResPattern_c0h1d0t2(t *testing.T) { testTransportResPattern(t, f0, f1, d0, f2) }
-func TestTransportResPattern_c0h1d1t0(t *testing.T) { testTransportResPattern(t, f0, f1, d1, f0) }
-func TestTransportResPattern_c0h1d1t1(t *testing.T) { testTransportResPattern(t, f0, f1, d1, f1) }
-func TestTransportResPattern_c0h1d1t2(t *testing.T) { testTransportResPattern(t, f0, f1, d1, f2) }
-func TestTransportResPattern_c0h2d0t0(t *testing.T) { testTransportResPattern(t, f0, f2, d0, f0) }
-func TestTransportResPattern_c0h2d0t1(t *testing.T) { testTransportResPattern(t, f0, f2, d0, f1) }
-func TestTransportResPattern_c0h2d0t2(t *testing.T) { testTransportResPattern(t, f0, f2, d0, f2) }
-func TestTransportResPattern_c0h2d1t0(t *testing.T) { testTransportResPattern(t, f0, f2, d1, f0) }
-func TestTransportResPattern_c0h2d1t1(t *testing.T) { testTransportResPattern(t, f0, f2, d1, f1) }
-func TestTransportResPattern_c0h2d1t2(t *testing.T) { testTransportResPattern(t, f0, f2, d1, f2) }
-func TestTransportResPattern_c1h1d0t0(t *testing.T) { testTransportResPattern(t, f1, f1, d0, f0) }
-func TestTransportResPattern_c1h1d0t1(t *testing.T) { testTransportResPattern(t, f1, f1, d0, f1) }
-func TestTransportResPattern_c1h1d0t2(t *testing.T) { testTransportResPattern(t, f1, f1, d0, f2) }
-func TestTransportResPattern_c1h1d1t0(t *testing.T) { testTransportResPattern(t, f1, f1, d1, f0) }
-func TestTransportResPattern_c1h1d1t1(t *testing.T) { testTransportResPattern(t, f1, f1, d1, f1) }
-func TestTransportResPattern_c1h1d1t2(t *testing.T) { testTransportResPattern(t, f1, f1, d1, f2) }
-func TestTransportResPattern_c1h2d0t0(t *testing.T) { testTransportResPattern(t, f1, f2, d0, f0) }
-func TestTransportResPattern_c1h2d0t1(t *testing.T) { testTransportResPattern(t, f1, f2, d0, f1) }
-func TestTransportResPattern_c1h2d0t2(t *testing.T) { testTransportResPattern(t, f1, f2, d0, f2) }
-func TestTransportResPattern_c1h2d1t0(t *testing.T) { testTransportResPattern(t, f1, f2, d1, f0) }
-func TestTransportResPattern_c1h2d1t1(t *testing.T) { testTransportResPattern(t, f1, f2, d1, f1) }
-func TestTransportResPattern_c1h2d1t2(t *testing.T) { testTransportResPattern(t, f1, f2, d1, f2) }
-func TestTransportResPattern_c2h1d0t0(t *testing.T) { testTransportResPattern(t, f2, f1, d0, f0) }
-func TestTransportResPattern_c2h1d0t1(t *testing.T) { testTransportResPattern(t, f2, f1, d0, f1) }
-func TestTransportResPattern_c2h1d0t2(t *testing.T) { testTransportResPattern(t, f2, f1, d0, f2) }
-func TestTransportResPattern_c2h1d1t0(t *testing.T) { testTransportResPattern(t, f2, f1, d1, f0) }
-func TestTransportResPattern_c2h1d1t1(t *testing.T) { testTransportResPattern(t, f2, f1, d1, f1) }
-func TestTransportResPattern_c2h1d1t2(t *testing.T) { testTransportResPattern(t, f2, f1, d1, f2) }
-func TestTransportResPattern_c2h2d0t0(t *testing.T) { testTransportResPattern(t, f2, f2, d0, f0) }
-func TestTransportResPattern_c2h2d0t1(t *testing.T) { testTransportResPattern(t, f2, f2, d0, f1) }
-func TestTransportResPattern_c2h2d0t2(t *testing.T) { testTransportResPattern(t, f2, f2, d0, f2) }
-func TestTransportResPattern_c2h2d1t0(t *testing.T) { testTransportResPattern(t, f2, f2, d1, f0) }
-func TestTransportResPattern_c2h2d1t1(t *testing.T) { testTransportResPattern(t, f2, f2, d1, f1) }
-func TestTransportResPattern_c2h2d1t2(t *testing.T) { testTransportResPattern(t, f2, f2, d1, f2) }
-
-func testTransportResPattern(t *testing.T, expect100Continue, resHeader headerType, withData bool, trailers headerType) {
- const reqBody = "some request body"
- const resBody = "some response body"
-
- if resHeader == noHeader {
- // TODO: test 100-continue followed by immediate
- // server stream reset, without headers in the middle?
- panic("invalid combination")
- }
-
- ct := newClientTester(t)
- ct.client = func() error {
- req, _ := http.NewRequest("POST", "https://dummy.tld/", strings.NewReader(reqBody))
- if expect100Continue != noHeader {
- req.Header.Set("Expect", "100-continue")
- }
- res, err := ct.tr.RoundTrip(req)
- if err != nil {
- return fmt.Errorf("RoundTrip: %v", err)
- }
- defer res.Body.Close()
- if res.StatusCode != 200 {
- return fmt.Errorf("status code = %v; want 200", res.StatusCode)
- }
- slurp, err := ioutil.ReadAll(res.Body)
- if err != nil {
- return fmt.Errorf("Slurp: %v", err)
- }
- wantBody := resBody
- if !withData {
- wantBody = ""
- }
- if string(slurp) != wantBody {
- return fmt.Errorf("body = %q; want %q", slurp, wantBody)
- }
- if trailers == noHeader {
- if len(res.Trailer) > 0 {
- t.Errorf("Trailer = %v; want none", res.Trailer)
- }
- } else {
- want := http.Header{"Some-Trailer": {"some-value"}}
- if !reflect.DeepEqual(res.Trailer, want) {
- t.Errorf("Trailer = %v; want %v", res.Trailer, want)
- }
- }
- return nil
- }
- ct.server = func() error {
- ct.greet()
- var buf bytes.Buffer
- enc := hpack.NewEncoder(&buf)
-
- for {
- f, err := ct.fr.ReadFrame()
- if err != nil {
- return err
- }
- switch f := f.(type) {
- case *WindowUpdateFrame, *SettingsFrame:
- case *DataFrame:
- // ignore for now.
- case *HeadersFrame:
- endStream := false
- send := func(mode headerType) {
- hbf := buf.Bytes()
- switch mode {
- case oneHeader:
- ct.fr.WriteHeaders(HeadersFrameParam{
- StreamID: f.StreamID,
- EndHeaders: true,
- EndStream: endStream,
- BlockFragment: hbf,
- })
- case splitHeader:
- if len(hbf) < 2 {
- panic("too small")
- }
- ct.fr.WriteHeaders(HeadersFrameParam{
- StreamID: f.StreamID,
- EndHeaders: false,
- EndStream: endStream,
- BlockFragment: hbf[:1],
- })
- ct.fr.WriteContinuation(f.StreamID, true, hbf[1:])
- default:
- panic("bogus mode")
- }
- }
- if expect100Continue != noHeader {
- buf.Reset()
- enc.WriteField(hpack.HeaderField{Name: ":status", Value: "100"})
- send(expect100Continue)
- }
- // Response headers (1+ frames; 1 or 2 in this test, but never 0)
- {
- buf.Reset()
- enc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"})
- enc.WriteField(hpack.HeaderField{Name: "x-foo", Value: "blah"})
- enc.WriteField(hpack.HeaderField{Name: "x-bar", Value: "more"})
- if trailers != noHeader {
- enc.WriteField(hpack.HeaderField{Name: "trailer", Value: "some-trailer"})
- }
- endStream = withData == false && trailers == noHeader
- send(resHeader)
- }
- if withData {
- endStream = trailers == noHeader
- ct.fr.WriteData(f.StreamID, endStream, []byte(resBody))
- }
- if trailers != noHeader {
- endStream = true
- buf.Reset()
- enc.WriteField(hpack.HeaderField{Name: "some-trailer", Value: "some-value"})
- send(trailers)
- }
- return nil
- }
- }
- }
- ct.run()
-}
-
-func TestTransportReceiveUndeclaredTrailer(t *testing.T) {
- ct := newClientTester(t)
- ct.client = func() error {
- req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
- res, err := ct.tr.RoundTrip(req)
- if err != nil {
- return fmt.Errorf("RoundTrip: %v", err)
- }
- defer res.Body.Close()
- if res.StatusCode != 200 {
- return fmt.Errorf("status code = %v; want 200", res.StatusCode)
- }
- slurp, err := ioutil.ReadAll(res.Body)
- if err != nil {
- return fmt.Errorf("res.Body ReadAll error = %q, %v; want %v", slurp, err, nil)
- }
- if len(slurp) > 0 {
- return fmt.Errorf("body = %q; want nothing", slurp)
- }
- if _, ok := res.Trailer["Some-Trailer"]; !ok {
- return fmt.Errorf("expected Some-Trailer")
- }
- return nil
- }
- ct.server = func() error {
- ct.greet()
-
- var n int
- var hf *HeadersFrame
- for hf == nil && n < 10 {
- f, err := ct.fr.ReadFrame()
- if err != nil {
- return err
- }
- hf, _ = f.(*HeadersFrame)
- n++
- }
-
- var buf bytes.Buffer
- enc := hpack.NewEncoder(&buf)
-
- // send headers without Trailer header
- enc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"})
- ct.fr.WriteHeaders(HeadersFrameParam{
- StreamID: hf.StreamID,
- EndHeaders: true,
- EndStream: false,
- BlockFragment: buf.Bytes(),
- })
-
- // send trailers
- buf.Reset()
- enc.WriteField(hpack.HeaderField{Name: "some-trailer", Value: "I'm an undeclared Trailer!"})
- ct.fr.WriteHeaders(HeadersFrameParam{
- StreamID: hf.StreamID,
- EndHeaders: true,
- EndStream: true,
- BlockFragment: buf.Bytes(),
- })
- return nil
- }
- ct.run()
-}
-
-func TestTransportInvalidTrailer_Pseudo1(t *testing.T) {
- testTransportInvalidTrailer_Pseudo(t, oneHeader)
-}
-func TestTransportInvalidTrailer_Pseudo2(t *testing.T) {
- testTransportInvalidTrailer_Pseudo(t, splitHeader)
-}
-func testTransportInvalidTrailer_Pseudo(t *testing.T, trailers headerType) {
- testInvalidTrailer(t, trailers, errPseudoTrailers, func(enc *hpack.Encoder) {
- enc.WriteField(hpack.HeaderField{Name: ":colon", Value: "foo"})
- enc.WriteField(hpack.HeaderField{Name: "foo", Value: "bar"})
- })
-}
-
-func TestTransportInvalidTrailer_Capital1(t *testing.T) {
- testTransportInvalidTrailer_Capital(t, oneHeader)
-}
-func TestTransportInvalidTrailer_Capital2(t *testing.T) {
- testTransportInvalidTrailer_Capital(t, splitHeader)
-}
-func testTransportInvalidTrailer_Capital(t *testing.T, trailers headerType) {
- testInvalidTrailer(t, trailers, errInvalidHeaderFieldName, func(enc *hpack.Encoder) {
- enc.WriteField(hpack.HeaderField{Name: "foo", Value: "bar"})
- enc.WriteField(hpack.HeaderField{Name: "Capital", Value: "bad"})
- })
-}
-func TestTransportInvalidTrailer_EmptyFieldName(t *testing.T) {
- testInvalidTrailer(t, oneHeader, errInvalidHeaderFieldName, func(enc *hpack.Encoder) {
- enc.WriteField(hpack.HeaderField{Name: "", Value: "bad"})
- })
-}
-func TestTransportInvalidTrailer_BinaryFieldValue(t *testing.T) {
- testInvalidTrailer(t, oneHeader, errInvalidHeaderFieldValue, func(enc *hpack.Encoder) {
- enc.WriteField(hpack.HeaderField{Name: "", Value: "has\nnewline"})
- })
-}
-
-func testInvalidTrailer(t *testing.T, trailers headerType, wantErr error, writeTrailer func(*hpack.Encoder)) {
- ct := newClientTester(t)
- ct.client = func() error {
- req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
- res, err := ct.tr.RoundTrip(req)
- if err != nil {
- return fmt.Errorf("RoundTrip: %v", err)
- }
- defer res.Body.Close()
- if res.StatusCode != 200 {
- return fmt.Errorf("status code = %v; want 200", res.StatusCode)
- }
- slurp, err := ioutil.ReadAll(res.Body)
- if err != wantErr {
- return fmt.Errorf("res.Body ReadAll error = %q, %v; want %v", slurp, err, wantErr)
- }
- if len(slurp) > 0 {
- return fmt.Errorf("body = %q; want nothing", slurp)
- }
- return nil
- }
- ct.server = func() error {
- ct.greet()
- var buf bytes.Buffer
- enc := hpack.NewEncoder(&buf)
-
- for {
- f, err := ct.fr.ReadFrame()
- if err != nil {
- return err
- }
- switch f := f.(type) {
- case *HeadersFrame:
- var endStream bool
- send := func(mode headerType) {
- hbf := buf.Bytes()
- switch mode {
- case oneHeader:
- ct.fr.WriteHeaders(HeadersFrameParam{
- StreamID: f.StreamID,
- EndHeaders: true,
- EndStream: endStream,
- BlockFragment: hbf,
- })
- case splitHeader:
- if len(hbf) < 2 {
- panic("too small")
- }
- ct.fr.WriteHeaders(HeadersFrameParam{
- StreamID: f.StreamID,
- EndHeaders: false,
- EndStream: endStream,
- BlockFragment: hbf[:1],
- })
- ct.fr.WriteContinuation(f.StreamID, true, hbf[1:])
- default:
- panic("bogus mode")
- }
- }
- // Response headers (1+ frames; 1 or 2 in this test, but never 0)
- {
- buf.Reset()
- enc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"})
- enc.WriteField(hpack.HeaderField{Name: "trailer", Value: "declared"})
- endStream = false
- send(oneHeader)
- }
- // Trailers:
- {
- endStream = true
- buf.Reset()
- writeTrailer(enc)
- send(trailers)
- }
- return nil
- }
- }
- }
- ct.run()
-}
-
-func TestTransportChecksResponseHeaderListSize(t *testing.T) {
- ct := newClientTester(t)
- ct.client = func() error {
- req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
- res, err := ct.tr.RoundTrip(req)
- if err != errResponseHeaderListSize {
- if res != nil {
- res.Body.Close()
- }
- size := int64(0)
- for k, vv := range res.Header {
- for _, v := range vv {
- size += int64(len(k)) + int64(len(v)) + 32
- }
- }
- return fmt.Errorf("RoundTrip Error = %v (and %d bytes of response headers); want errResponseHeaderListSize", err, size)
- }
- return nil
- }
- ct.server = func() error {
- ct.greet()
- var buf bytes.Buffer
- enc := hpack.NewEncoder(&buf)
-
- for {
- f, err := ct.fr.ReadFrame()
- if err != nil {
- return err
- }
- switch f := f.(type) {
- case *HeadersFrame:
- enc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"})
- large := strings.Repeat("a", 1<<10)
- for i := 0; i < 5042; i++ {
- enc.WriteField(hpack.HeaderField{Name: large, Value: large})
- }
- if size, want := buf.Len(), 6329; size != want {
- // Note: this number might change if
- // our hpack implementation
- // changes. That's fine. This is
- // just a sanity check that our
- // response can fit in a single
- // header block fragment frame.
- return fmt.Errorf("encoding over 10MB of duplicate keypairs took %d bytes; expected %d", size, want)
- }
- ct.fr.WriteHeaders(HeadersFrameParam{
- StreamID: f.StreamID,
- EndHeaders: true,
- EndStream: true,
- BlockFragment: buf.Bytes(),
- })
- return nil
- }
- }
- }
- ct.run()
-}
-
-// Test that the the Transport returns a typed error from Response.Body.Read calls
-// when the server sends an error. (here we use a panic, since that should generate
-// a stream error, but others like cancel should be similar)
-func TestTransportBodyReadErrorType(t *testing.T) {
- doPanic := make(chan bool, 1)
- st := newServerTester(t,
- func(w http.ResponseWriter, r *http.Request) {
- w.(http.Flusher).Flush() // force headers out
- <-doPanic
- panic("boom")
- },
- optOnlyServer,
- optQuiet,
- )
- 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)
- }
- defer res.Body.Close()
- doPanic <- true
- buf := make([]byte, 100)
- n, err := res.Body.Read(buf)
- want := StreamError{StreamID: 0x1, Code: 0x2}
- if !reflect.DeepEqual(want, err) {
- t.Errorf("Read = %v, %#v; want error %#v", n, err, want)
- }
-}
-
-// golang.org/issue/13924
-// This used to fail after many iterations, especially with -race:
-// go test -v -run=TestTransportDoubleCloseOnWriteError -count=500 -race
-func TestTransportDoubleCloseOnWriteError(t *testing.T) {
- var (
- mu sync.Mutex
- conn net.Conn // to close if set
- )
-
- st := newServerTester(t,
- func(w http.ResponseWriter, r *http.Request) {
- mu.Lock()
- defer mu.Unlock()
- if conn != nil {
- conn.Close()
- }
- },
- optOnlyServer,
- )
- defer st.Close()
-
- tr := &Transport{
- TLSClientConfig: tlsConfigInsecure,
- DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
- tc, err := tls.Dial(network, addr, cfg)
- if err != nil {
- return nil, err
- }
- mu.Lock()
- defer mu.Unlock()
- conn = tc
- return tc, nil
- },
- }
- defer tr.CloseIdleConnections()
- c := &http.Client{Transport: tr}
- c.Get(st.ts.URL)
-}
-
-// Test that the http1 Transport.DisableKeepAlives option is respected
-// and connections are closed as soon as idle.
-// See golang.org/issue/14008
-func TestTransportDisableKeepAlives(t *testing.T) {
- st := newServerTester(t,
- func(w http.ResponseWriter, r *http.Request) {
- io.WriteString(w, "hi")
- },
- optOnlyServer,
- )
- defer st.Close()
-
- connClosed := make(chan struct{}) // closed on tls.Conn.Close
- tr := &Transport{
- t1: &http.Transport{
- DisableKeepAlives: true,
- },
- TLSClientConfig: tlsConfigInsecure,
- DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
- tc, err := tls.Dial(network, addr, cfg)
- if err != nil {
- return nil, err
- }
- return ¬eCloseConn{Conn: tc, closefn: func() { close(connClosed) }}, nil
- },
- }
- c := &http.Client{Transport: tr}
- res, err := c.Get(st.ts.URL)
- if err != nil {
- t.Fatal(err)
- }
- if _, err := ioutil.ReadAll(res.Body); err != nil {
- t.Fatal(err)
- }
- defer res.Body.Close()
-
- select {
- case <-connClosed:
- case <-time.After(1 * time.Second):
- t.Errorf("timeout")
- }
-
-}
-
-// Test concurrent requests with Transport.DisableKeepAlives. We can share connections,
-// but when things are totally idle, it still needs to close.
-func TestTransportDisableKeepAlives_Concurrency(t *testing.T) {
- const D = 25 * time.Millisecond
- st := newServerTester(t,
- func(w http.ResponseWriter, r *http.Request) {
- time.Sleep(D)
- io.WriteString(w, "hi")
- },
- optOnlyServer,
- )
- defer st.Close()
-
- var dials int32
- var conns sync.WaitGroup
- tr := &Transport{
- t1: &http.Transport{
- DisableKeepAlives: true,
- },
- TLSClientConfig: tlsConfigInsecure,
- DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
- tc, err := tls.Dial(network, addr, cfg)
- if err != nil {
- return nil, err
- }
- atomic.AddInt32(&dials, 1)
- conns.Add(1)
- return ¬eCloseConn{Conn: tc, closefn: func() { conns.Done() }}, nil
- },
- }
- c := &http.Client{Transport: tr}
- var reqs sync.WaitGroup
- const N = 20
- for i := 0; i < N; i++ {
- reqs.Add(1)
- if i == N-1 {
- // For the final request, try to make all the
- // others close. This isn't verified in the
- // count, other than the Log statement, since
- // it's so timing dependent. This test is
- // really to make sure we don't interrupt a
- // valid request.
- time.Sleep(D * 2)
- }
- go func() {
- defer reqs.Done()
- res, err := c.Get(st.ts.URL)
- if err != nil {
- t.Error(err)
- return
- }
- if _, err := ioutil.ReadAll(res.Body); err != nil {
- t.Error(err)
- return
- }
- res.Body.Close()
- }()
- }
- reqs.Wait()
- conns.Wait()
- t.Logf("did %d dials, %d requests", atomic.LoadInt32(&dials), N)
-}
-
-type noteCloseConn struct {
- net.Conn
- onceClose sync.Once
- closefn func()
-}
-
-func (c *noteCloseConn) Close() error {
- c.onceClose.Do(c.closefn)
- return c.Conn.Close()
-}
-
-func isTimeout(err error) bool {
- switch err := err.(type) {
- case nil:
- return false
- case *url.Error:
- return isTimeout(err.Err)
- case net.Error:
- return err.Timeout()
- }
- return false
-}
-
-// Test that the http1 Transport.ResponseHeaderTimeout option and cancel is sent.
-func TestTransportResponseHeaderTimeout_NoBody(t *testing.T) {
- testTransportResponseHeaderTimeout(t, false)
-}
-func TestTransportResponseHeaderTimeout_Body(t *testing.T) {
- testTransportResponseHeaderTimeout(t, true)
-}
-
-func testTransportResponseHeaderTimeout(t *testing.T, body bool) {
- ct := newClientTester(t)
- ct.tr.t1 = &http.Transport{
- ResponseHeaderTimeout: 5 * time.Millisecond,
- }
- ct.client = func() error {
- c := &http.Client{Transport: ct.tr}
- var err error
- var n int64
- const bodySize = 4 << 20
- if body {
- _, err = c.Post("https://dummy.tld/", "text/foo", io.LimitReader(countingReader{&n}, bodySize))
- } else {
- _, err = c.Get("https://dummy.tld/")
- }
- if !isTimeout(err) {
- t.Errorf("client expected timeout error; got %#v", err)
- }
- if body && n != bodySize {
- t.Errorf("only read %d bytes of body; want %d", n, bodySize)
- }
- return nil
- }
- ct.server = func() error {
- ct.greet()
- for {
- f, err := ct.fr.ReadFrame()
- if err != nil {
- t.Logf("ReadFrame: %v", err)
- return nil
- }
- switch f := f.(type) {
- case *DataFrame:
- dataLen := len(f.Data())
- if dataLen > 0 {
- if err := ct.fr.WriteWindowUpdate(0, uint32(dataLen)); err != nil {
- return err
- }
- if err := ct.fr.WriteWindowUpdate(f.StreamID, uint32(dataLen)); err != nil {
- return err
- }
- }
- case *RSTStreamFrame:
- if f.StreamID == 1 && f.ErrCode == ErrCodeCancel {
- return nil
- }
- }
- }
- return nil
- }
- ct.run()
-}
-
-func TestTransportDisableCompression(t *testing.T) {
- const body = "sup"
- st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
- want := http.Header{
- "User-Agent": []string{"Go-http-client/2.0"},
- }
- if !reflect.DeepEqual(r.Header, want) {
- t.Errorf("request headers = %v; want %v", r.Header, want)
- }
- }, optOnlyServer)
- defer st.Close()
-
- tr := &Transport{
- TLSClientConfig: tlsConfigInsecure,
- t1: &http.Transport{
- DisableCompression: true,
- },
- }
- defer tr.CloseIdleConnections()
-
- req, err := http.NewRequest("GET", st.ts.URL, nil)
- if err != nil {
- t.Fatal(err)
- }
- res, err := tr.RoundTrip(req)
- if err != nil {
- t.Fatal(err)
- }
- defer res.Body.Close()
-}
diff --git a/Godeps/_workspace/src/golang.org/x/net/http2/write.go b/Godeps/_workspace/src/golang.org/x/net/http2/write.go
index 1be0a8dbc3c..2ebba1b73aa 100644
--- a/Godeps/_workspace/src/golang.org/x/net/http2/write.go
+++ b/Godeps/_workspace/src/golang.org/x/net/http2/write.go
@@ -241,8 +241,18 @@ func encodeHeaders(enc *hpack.Encoder, h http.Header, keys []string) {
for _, k := range keys {
vv := h[k]
k = lowerHeader(k)
+ if !validHeaderFieldName(k) {
+ // TODO: return an error? golang.org/issue/14048
+ // For now just omit it.
+ continue
+ }
isTE := k == "transfer-encoding"
for _, v := range vv {
+ if !validHeaderFieldValue(v) {
+ // TODO: return an error? golang.org/issue/14048
+ // For now just omit it.
+ continue
+ }
// TODO: more of "8.1.2.2 Connection-Specific Header Fields"
if isTE && v != "trailers" {
continue
diff --git a/Godeps/_workspace/src/golang.org/x/net/http2/z_spec_test.go b/Godeps/_workspace/src/golang.org/x/net/http2/z_spec_test.go
deleted file mode 100644
index e0f420a1884..00000000000
--- a/Godeps/_workspace/src/golang.org/x/net/http2/z_spec_test.go
+++ /dev/null
@@ -1,356 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package http2
-
-import (
- "bytes"
- "encoding/xml"
- "flag"
- "fmt"
- "io"
- "os"
- "reflect"
- "regexp"
- "sort"
- "strconv"
- "strings"
- "sync"
- "testing"
-)
-
-var coverSpec = flag.Bool("coverspec", false, "Run spec coverage tests")
-
-// The global map of sentence coverage for the http2 spec.
-var defaultSpecCoverage specCoverage
-
-var loadSpecOnce sync.Once
-
-func loadSpec() {
- if f, err := os.Open("testdata/draft-ietf-httpbis-http2.xml"); err != nil {
- panic(err)
- } else {
- defaultSpecCoverage = readSpecCov(f)
- f.Close()
- }
-}
-
-// covers marks all sentences for section sec in defaultSpecCoverage. Sentences not
-// "covered" will be included in report outputed by TestSpecCoverage.
-func covers(sec, sentences string) {
- loadSpecOnce.Do(loadSpec)
- defaultSpecCoverage.cover(sec, sentences)
-}
-
-type specPart struct {
- section string
- sentence string
-}
-
-func (ss specPart) Less(oo specPart) bool {
- atoi := func(s string) int {
- n, err := strconv.Atoi(s)
- if err != nil {
- panic(err)
- }
- return n
- }
- a := strings.Split(ss.section, ".")
- b := strings.Split(oo.section, ".")
- for len(a) > 0 {
- if len(b) == 0 {
- return false
- }
- x, y := atoi(a[0]), atoi(b[0])
- if x == y {
- a, b = a[1:], b[1:]
- continue
- }
- return x < y
- }
- if len(b) > 0 {
- return true
- }
- return false
-}
-
-type bySpecSection []specPart
-
-func (a bySpecSection) Len() int { return len(a) }
-func (a bySpecSection) Less(i, j int) bool { return a[i].Less(a[j]) }
-func (a bySpecSection) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
-
-type specCoverage struct {
- coverage map[specPart]bool
- d *xml.Decoder
-}
-
-func joinSection(sec []int) string {
- s := fmt.Sprintf("%d", sec[0])
- for _, n := range sec[1:] {
- s = fmt.Sprintf("%s.%d", s, n)
- }
- return s
-}
-
-func (sc specCoverage) readSection(sec []int) {
- var (
- buf = new(bytes.Buffer)
- sub = 0
- )
- for {
- tk, err := sc.d.Token()
- if err != nil {
- if err == io.EOF {
- return
- }
- panic(err)
- }
- switch v := tk.(type) {
- case xml.StartElement:
- if skipElement(v) {
- if err := sc.d.Skip(); err != nil {
- panic(err)
- }
- if v.Name.Local == "section" {
- sub++
- }
- break
- }
- switch v.Name.Local {
- case "section":
- sub++
- sc.readSection(append(sec, sub))
- case "xref":
- buf.Write(sc.readXRef(v))
- }
- case xml.CharData:
- if len(sec) == 0 {
- break
- }
- buf.Write(v)
- case xml.EndElement:
- if v.Name.Local == "section" {
- sc.addSentences(joinSection(sec), buf.String())
- return
- }
- }
- }
-}
-
-func (sc specCoverage) readXRef(se xml.StartElement) []byte {
- var b []byte
- for {
- tk, err := sc.d.Token()
- if err != nil {
- panic(err)
- }
- switch v := tk.(type) {
- case xml.CharData:
- if b != nil {
- panic("unexpected CharData")
- }
- b = []byte(string(v))
- case xml.EndElement:
- if v.Name.Local != "xref" {
- panic("expected ")
- }
- if b != nil {
- return b
- }
- sig := attrSig(se)
- switch sig {
- case "target":
- return []byte(fmt.Sprintf("[%s]", attrValue(se, "target")))
- case "fmt-of,rel,target", "fmt-,,rel,target":
- return []byte(fmt.Sprintf("[%s, %s]", attrValue(se, "target"), attrValue(se, "rel")))
- case "fmt-of,sec,target", "fmt-,,sec,target":
- return []byte(fmt.Sprintf("[section %s of %s]", attrValue(se, "sec"), attrValue(se, "target")))
- case "fmt-of,rel,sec,target":
- return []byte(fmt.Sprintf("[section %s of %s, %s]", attrValue(se, "sec"), attrValue(se, "target"), attrValue(se, "rel")))
- default:
- panic(fmt.Sprintf("unknown attribute signature %q in %#v", sig, fmt.Sprintf("%#v", se)))
- }
- default:
- panic(fmt.Sprintf("unexpected tag %q", v))
- }
- }
-}
-
-var skipAnchor = map[string]bool{
- "intro": true,
- "Overview": true,
-}
-
-var skipTitle = map[string]bool{
- "Acknowledgements": true,
- "Change Log": true,
- "Document Organization": true,
- "Conventions and Terminology": true,
-}
-
-func skipElement(s xml.StartElement) bool {
- switch s.Name.Local {
- case "artwork":
- return true
- case "section":
- for _, attr := range s.Attr {
- switch attr.Name.Local {
- case "anchor":
- if skipAnchor[attr.Value] || strings.HasPrefix(attr.Value, "changes.since.") {
- return true
- }
- case "title":
- if skipTitle[attr.Value] {
- return true
- }
- }
- }
- }
- return false
-}
-
-func readSpecCov(r io.Reader) specCoverage {
- sc := specCoverage{
- coverage: map[specPart]bool{},
- d: xml.NewDecoder(r)}
- sc.readSection(nil)
- return sc
-}
-
-func (sc specCoverage) addSentences(sec string, sentence string) {
- for _, s := range parseSentences(sentence) {
- sc.coverage[specPart{sec, s}] = false
- }
-}
-
-func (sc specCoverage) cover(sec string, sentence string) {
- for _, s := range parseSentences(sentence) {
- p := specPart{sec, s}
- if _, ok := sc.coverage[p]; !ok {
- panic(fmt.Sprintf("Not found in spec: %q, %q", sec, s))
- }
- sc.coverage[specPart{sec, s}] = true
- }
-
-}
-
-var whitespaceRx = regexp.MustCompile(`\s+`)
-
-func parseSentences(sens string) []string {
- sens = strings.TrimSpace(sens)
- if sens == "" {
- return nil
- }
- ss := strings.Split(whitespaceRx.ReplaceAllString(sens, " "), ". ")
- for i, s := range ss {
- s = strings.TrimSpace(s)
- if !strings.HasSuffix(s, ".") {
- s += "."
- }
- ss[i] = s
- }
- return ss
-}
-
-func TestSpecParseSentences(t *testing.T) {
- tests := []struct {
- ss string
- want []string
- }{
- {"Sentence 1. Sentence 2.",
- []string{
- "Sentence 1.",
- "Sentence 2.",
- }},
- {"Sentence 1. \nSentence 2.\tSentence 3.",
- []string{
- "Sentence 1.",
- "Sentence 2.",
- "Sentence 3.",
- }},
- }
-
- for i, tt := range tests {
- got := parseSentences(tt.ss)
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("%d: got = %q, want %q", i, got, tt.want)
- }
- }
-}
-
-func TestSpecCoverage(t *testing.T) {
- if !*coverSpec {
- t.Skip()
- }
-
- loadSpecOnce.Do(loadSpec)
-
- var (
- list []specPart
- cv = defaultSpecCoverage.coverage
- total = len(cv)
- complete = 0
- )
-
- for sp, touched := range defaultSpecCoverage.coverage {
- if touched {
- complete++
- } else {
- list = append(list, sp)
- }
- }
- sort.Stable(bySpecSection(list))
-
- if testing.Short() && len(list) > 5 {
- list = list[:5]
- }
-
- for _, p := range list {
- t.Errorf("\tSECTION %s: %s", p.section, p.sentence)
- }
-
- t.Logf("%d/%d (%d%%) sentances covered", complete, total, (complete/total)*100)
-}
-
-func attrSig(se xml.StartElement) string {
- var names []string
- for _, attr := range se.Attr {
- if attr.Name.Local == "fmt" {
- names = append(names, "fmt-"+attr.Value)
- } else {
- names = append(names, attr.Name.Local)
- }
- }
- sort.Strings(names)
- return strings.Join(names, ",")
-}
-
-func attrValue(se xml.StartElement, attr string) string {
- for _, a := range se.Attr {
- if a.Name.Local == attr {
- return a.Value
- }
- }
- panic("unknown attribute " + attr)
-}
-
-func TestSpecPartLess(t *testing.T) {
- tests := []struct {
- sec1, sec2 string
- want bool
- }{
- {"6.2.1", "6.2", false},
- {"6.2", "6.2.1", true},
- {"6.10", "6.10.1", true},
- {"6.10", "6.1.1", false}, // 10, not 1
- {"6.1", "6.1", false}, // equal, so not less
- }
- for _, tt := range tests {
- got := (specPart{tt.sec1, "foo"}).Less(specPart{tt.sec2, "foo"})
- if got != tt.want {
- t.Errorf("Less(%q, %q) = %v; want %v", tt.sec1, tt.sec2, got, tt.want)
- }
- }
-}
diff --git a/Godeps/_workspace/src/golang.org/x/net/internal/timeseries/timeseries_test.go b/Godeps/_workspace/src/golang.org/x/net/internal/timeseries/timeseries_test.go
deleted file mode 100644
index 66325a912a8..00000000000
--- a/Godeps/_workspace/src/golang.org/x/net/internal/timeseries/timeseries_test.go
+++ /dev/null
@@ -1,170 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package timeseries
-
-import (
- "math"
- "testing"
- "time"
-)
-
-func isNear(x *Float, y float64, tolerance float64) bool {
- return math.Abs(x.Value()-y) < tolerance
-}
-
-func isApproximate(x *Float, y float64) bool {
- return isNear(x, y, 1e-2)
-}
-
-func checkApproximate(t *testing.T, o Observable, y float64) {
- x := o.(*Float)
- if !isApproximate(x, y) {
- t.Errorf("Wanted %g, got %g", y, x.Value())
- }
-}
-
-func checkNear(t *testing.T, o Observable, y, tolerance float64) {
- x := o.(*Float)
- if !isNear(x, y, tolerance) {
- t.Errorf("Wanted %g +- %g, got %g", y, tolerance, x.Value())
- }
-}
-
-var baseTime = time.Date(2013, 1, 1, 0, 0, 0, 0, time.UTC)
-
-func tu(s int64) time.Time {
- return baseTime.Add(time.Duration(s) * time.Second)
-}
-
-func tu2(s int64, ns int64) time.Time {
- return baseTime.Add(time.Duration(s)*time.Second + time.Duration(ns)*time.Nanosecond)
-}
-
-func TestBasicTimeSeries(t *testing.T) {
- ts := NewTimeSeries(NewFloat)
- fo := new(Float)
- *fo = Float(10)
- ts.AddWithTime(fo, tu(1))
- ts.AddWithTime(fo, tu(1))
- ts.AddWithTime(fo, tu(1))
- ts.AddWithTime(fo, tu(1))
- checkApproximate(t, ts.Range(tu(0), tu(1)), 40)
- checkApproximate(t, ts.Total(), 40)
- ts.AddWithTime(fo, tu(3))
- ts.AddWithTime(fo, tu(3))
- ts.AddWithTime(fo, tu(3))
- checkApproximate(t, ts.Range(tu(0), tu(2)), 40)
- checkApproximate(t, ts.Range(tu(2), tu(4)), 30)
- checkApproximate(t, ts.Total(), 70)
- ts.AddWithTime(fo, tu(1))
- ts.AddWithTime(fo, tu(1))
- checkApproximate(t, ts.Range(tu(0), tu(2)), 60)
- checkApproximate(t, ts.Range(tu(2), tu(4)), 30)
- checkApproximate(t, ts.Total(), 90)
- *fo = Float(100)
- ts.AddWithTime(fo, tu(100))
- checkApproximate(t, ts.Range(tu(99), tu(100)), 100)
- checkApproximate(t, ts.Range(tu(0), tu(4)), 36)
- checkApproximate(t, ts.Total(), 190)
- *fo = Float(10)
- ts.AddWithTime(fo, tu(1))
- ts.AddWithTime(fo, tu(1))
- checkApproximate(t, ts.Range(tu(0), tu(4)), 44)
- checkApproximate(t, ts.Range(tu(37), tu2(100, 100e6)), 100)
- checkApproximate(t, ts.Range(tu(50), tu2(100, 100e6)), 100)
- checkApproximate(t, ts.Range(tu(99), tu2(100, 100e6)), 100)
- checkApproximate(t, ts.Total(), 210)
-
- for i, l := range ts.ComputeRange(tu(36), tu(100), 64) {
- if i == 63 {
- checkApproximate(t, l, 100)
- } else {
- checkApproximate(t, l, 0)
- }
- }
-
- checkApproximate(t, ts.Range(tu(0), tu(100)), 210)
- checkApproximate(t, ts.Range(tu(10), tu(100)), 100)
-
- for i, l := range ts.ComputeRange(tu(0), tu(100), 100) {
- if i < 10 {
- checkApproximate(t, l, 11)
- } else if i >= 90 {
- checkApproximate(t, l, 10)
- } else {
- checkApproximate(t, l, 0)
- }
- }
-}
-
-func TestFloat(t *testing.T) {
- f := Float(1)
- if g, w := f.String(), "1"; g != w {
- t.Errorf("Float(1).String = %q; want %q", g, w)
- }
- f2 := Float(2)
- var o Observable = &f2
- f.Add(o)
- if g, w := f.Value(), 3.0; g != w {
- t.Errorf("Float post-add = %v; want %v", g, w)
- }
- f.Multiply(2)
- if g, w := f.Value(), 6.0; g != w {
- t.Errorf("Float post-multiply = %v; want %v", g, w)
- }
- f.Clear()
- if g, w := f.Value(), 0.0; g != w {
- t.Errorf("Float post-clear = %v; want %v", g, w)
- }
- f.CopyFrom(&f2)
- if g, w := f.Value(), 2.0; g != w {
- t.Errorf("Float post-CopyFrom = %v; want %v", g, w)
- }
-}
-
-type mockClock struct {
- time time.Time
-}
-
-func (m *mockClock) Time() time.Time { return m.time }
-func (m *mockClock) Set(t time.Time) { m.time = t }
-
-const buckets = 6
-
-var testResolutions = []time.Duration{
- 10 * time.Second, // level holds one minute of observations
- 100 * time.Second, // level holds ten minutes of observations
- 10 * time.Minute, // level holds one hour of observations
-}
-
-// TestTimeSeries uses a small number of buckets to force a higher
-// error rate on approximations from the timeseries.
-type TestTimeSeries struct {
- timeSeries
-}
-
-func TestExpectedErrorRate(t *testing.T) {
- ts := new(TestTimeSeries)
- fake := new(mockClock)
- fake.Set(time.Now())
- ts.timeSeries.init(testResolutions, NewFloat, buckets, fake)
- for i := 1; i <= 61*61; i++ {
- fake.Set(fake.Time().Add(1 * time.Second))
- ob := Float(1)
- ts.AddWithTime(&ob, fake.Time())
-
- // The results should be accurate within one missing bucket (1/6) of the observations recorded.
- checkNear(t, ts.Latest(0, buckets), min(float64(i), 60), 10)
- checkNear(t, ts.Latest(1, buckets), min(float64(i), 600), 100)
- checkNear(t, ts.Latest(2, buckets), min(float64(i), 3600), 600)
- }
-}
-
-func min(a, b float64) float64 {
- if a < b {
- return a
- }
- return b
-}
diff --git a/Godeps/_workspace/src/golang.org/x/net/trace/histogram_test.go b/Godeps/_workspace/src/golang.org/x/net/trace/histogram_test.go
deleted file mode 100644
index d384b9332d9..00000000000
--- a/Godeps/_workspace/src/golang.org/x/net/trace/histogram_test.go
+++ /dev/null
@@ -1,325 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package trace
-
-import (
- "math"
- "testing"
-)
-
-type sumTest struct {
- value int64
- sum int64
- sumOfSquares float64
- total int64
-}
-
-var sumTests = []sumTest{
- {100, 100, 10000, 1},
- {50, 150, 12500, 2},
- {50, 200, 15000, 3},
- {50, 250, 17500, 4},
-}
-
-type bucketingTest struct {
- in int64
- log int
- bucket int
-}
-
-var bucketingTests = []bucketingTest{
- {0, 0, 0},
- {1, 1, 0},
- {2, 2, 1},
- {3, 2, 1},
- {4, 3, 2},
- {1000, 10, 9},
- {1023, 10, 9},
- {1024, 11, 10},
- {1000000, 20, 19},
-}
-
-type multiplyTest struct {
- in int64
- ratio float64
- expectedSum int64
- expectedTotal int64
- expectedSumOfSquares float64
-}
-
-var multiplyTests = []multiplyTest{
- {15, 2.5, 37, 2, 562.5},
- {128, 4.6, 758, 13, 77953.9},
-}
-
-type percentileTest struct {
- fraction float64
- expected int64
-}
-
-var percentileTests = []percentileTest{
- {0.25, 48},
- {0.5, 96},
- {0.6, 109},
- {0.75, 128},
- {0.90, 205},
- {0.95, 230},
- {0.99, 256},
-}
-
-func TestSum(t *testing.T) {
- var h histogram
-
- for _, test := range sumTests {
- h.addMeasurement(test.value)
- sum := h.sum
- if sum != test.sum {
- t.Errorf("h.Sum = %v WANT: %v", sum, test.sum)
- }
-
- sumOfSquares := h.sumOfSquares
- if sumOfSquares != test.sumOfSquares {
- t.Errorf("h.SumOfSquares = %v WANT: %v", sumOfSquares, test.sumOfSquares)
- }
-
- total := h.total()
- if total != test.total {
- t.Errorf("h.Total = %v WANT: %v", total, test.total)
- }
- }
-}
-
-func TestMultiply(t *testing.T) {
- var h histogram
- for i, test := range multiplyTests {
- h.addMeasurement(test.in)
- h.Multiply(test.ratio)
- if h.sum != test.expectedSum {
- t.Errorf("#%v: h.sum = %v WANT: %v", i, h.sum, test.expectedSum)
- }
- if h.total() != test.expectedTotal {
- t.Errorf("#%v: h.total = %v WANT: %v", i, h.total(), test.expectedTotal)
- }
- if h.sumOfSquares != test.expectedSumOfSquares {
- t.Errorf("#%v: h.SumOfSquares = %v WANT: %v", i, test.expectedSumOfSquares, h.sumOfSquares)
- }
- }
-}
-
-func TestBucketingFunctions(t *testing.T) {
- for _, test := range bucketingTests {
- log := log2(test.in)
- if log != test.log {
- t.Errorf("log2 = %v WANT: %v", log, test.log)
- }
-
- bucket := getBucket(test.in)
- if bucket != test.bucket {
- t.Errorf("getBucket = %v WANT: %v", bucket, test.bucket)
- }
- }
-}
-
-func TestAverage(t *testing.T) {
- a := new(histogram)
- average := a.average()
- if average != 0 {
- t.Errorf("Average of empty histogram was %v WANT: 0", average)
- }
-
- a.addMeasurement(1)
- a.addMeasurement(1)
- a.addMeasurement(3)
- const expected = float64(5) / float64(3)
- average = a.average()
-
- if !isApproximate(average, expected) {
- t.Errorf("Average = %g WANT: %v", average, expected)
- }
-}
-
-func TestStandardDeviation(t *testing.T) {
- a := new(histogram)
- add(a, 10, 1<<4)
- add(a, 10, 1<<5)
- add(a, 10, 1<<6)
- stdDev := a.standardDeviation()
- const expected = 19.95
-
- if !isApproximate(stdDev, expected) {
- t.Errorf("StandardDeviation = %v WANT: %v", stdDev, expected)
- }
-
- // No values
- a = new(histogram)
- stdDev = a.standardDeviation()
-
- if !isApproximate(stdDev, 0) {
- t.Errorf("StandardDeviation = %v WANT: 0", stdDev)
- }
-
- add(a, 1, 1<<4)
- if !isApproximate(stdDev, 0) {
- t.Errorf("StandardDeviation = %v WANT: 0", stdDev)
- }
-
- add(a, 10, 1<<4)
- if !isApproximate(stdDev, 0) {
- t.Errorf("StandardDeviation = %v WANT: 0", stdDev)
- }
-}
-
-func TestPercentileBoundary(t *testing.T) {
- a := new(histogram)
- add(a, 5, 1<<4)
- add(a, 10, 1<<6)
- add(a, 5, 1<<7)
-
- for _, test := range percentileTests {
- percentile := a.percentileBoundary(test.fraction)
- if percentile != test.expected {
- t.Errorf("h.PercentileBoundary (fraction=%v) = %v WANT: %v", test.fraction, percentile, test.expected)
- }
- }
-}
-
-func TestCopyFrom(t *testing.T) {
- a := histogram{5, 25, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
- 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38}, 4, -1}
- b := histogram{6, 36, []int64{2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
- 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39}, 5, -1}
-
- a.CopyFrom(&b)
-
- if a.String() != b.String() {
- t.Errorf("a.String = %s WANT: %s", a.String(), b.String())
- }
-}
-
-func TestClear(t *testing.T) {
- a := histogram{5, 25, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
- 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38}, 4, -1}
-
- a.Clear()
-
- expected := "0, 0.000000, 0, 0, []"
- if a.String() != expected {
- t.Errorf("a.String = %s WANT %s", a.String(), expected)
- }
-}
-
-func TestNew(t *testing.T) {
- a := histogram{5, 25, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
- 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38}, 4, -1}
- b := a.New()
-
- expected := "0, 0.000000, 0, 0, []"
- if b.(*histogram).String() != expected {
- t.Errorf("b.(*histogram).String = %s WANT: %s", b.(*histogram).String(), expected)
- }
-}
-
-func TestAdd(t *testing.T) {
- // The tests here depend on the associativity of addMeasurement and Add.
- // Add empty observation
- a := histogram{5, 25, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
- 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38}, 4, -1}
- b := a.New()
-
- expected := a.String()
- a.Add(b)
- if a.String() != expected {
- t.Errorf("a.String = %s WANT: %s", a.String(), expected)
- }
-
- // Add same bucketed value, no new buckets
- c := new(histogram)
- d := new(histogram)
- e := new(histogram)
- c.addMeasurement(12)
- d.addMeasurement(11)
- e.addMeasurement(12)
- e.addMeasurement(11)
- c.Add(d)
- if c.String() != e.String() {
- t.Errorf("c.String = %s WANT: %s", c.String(), e.String())
- }
-
- // Add bucketed values
- f := new(histogram)
- g := new(histogram)
- h := new(histogram)
- f.addMeasurement(4)
- f.addMeasurement(12)
- f.addMeasurement(100)
- g.addMeasurement(18)
- g.addMeasurement(36)
- g.addMeasurement(255)
- h.addMeasurement(4)
- h.addMeasurement(12)
- h.addMeasurement(100)
- h.addMeasurement(18)
- h.addMeasurement(36)
- h.addMeasurement(255)
- f.Add(g)
- if f.String() != h.String() {
- t.Errorf("f.String = %q WANT: %q", f.String(), h.String())
- }
-
- // add buckets to no buckets
- i := new(histogram)
- j := new(histogram)
- k := new(histogram)
- j.addMeasurement(18)
- j.addMeasurement(36)
- j.addMeasurement(255)
- k.addMeasurement(18)
- k.addMeasurement(36)
- k.addMeasurement(255)
- i.Add(j)
- if i.String() != k.String() {
- t.Errorf("i.String = %q WANT: %q", i.String(), k.String())
- }
-
- // add buckets to single value (no overlap)
- l := new(histogram)
- m := new(histogram)
- n := new(histogram)
- l.addMeasurement(0)
- m.addMeasurement(18)
- m.addMeasurement(36)
- m.addMeasurement(255)
- n.addMeasurement(0)
- n.addMeasurement(18)
- n.addMeasurement(36)
- n.addMeasurement(255)
- l.Add(m)
- if l.String() != n.String() {
- t.Errorf("l.String = %q WANT: %q", l.String(), n.String())
- }
-
- // mixed order
- o := new(histogram)
- p := new(histogram)
- o.addMeasurement(0)
- o.addMeasurement(2)
- o.addMeasurement(0)
- p.addMeasurement(0)
- p.addMeasurement(0)
- p.addMeasurement(2)
- if o.String() != p.String() {
- t.Errorf("o.String = %q WANT: %q", o.String(), p.String())
- }
-}
-
-func add(h *histogram, times int, val int64) {
- for i := 0; i < times; i++ {
- h.addMeasurement(val)
- }
-}
-
-func isApproximate(x, y float64) bool {
- return math.Abs(x-y) < 1e-2
-}
diff --git a/Godeps/_workspace/src/golang.org/x/net/trace/trace_test.go b/Godeps/_workspace/src/golang.org/x/net/trace/trace_test.go
deleted file mode 100644
index c2f5fcbaab4..00000000000
--- a/Godeps/_workspace/src/golang.org/x/net/trace/trace_test.go
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package trace
-
-import (
- "reflect"
- "testing"
-)
-
-type s struct{}
-
-func (s) String() string { return "lazy string" }
-
-// TestReset checks whether all the fields are zeroed after reset.
-func TestReset(t *testing.T) {
- tr := New("foo", "bar")
- tr.LazyLog(s{}, false)
- tr.LazyPrintf("%d", 1)
- tr.SetRecycler(func(_ interface{}) {})
- tr.SetTraceInfo(3, 4)
- tr.SetMaxEvents(100)
- tr.SetError()
- tr.Finish()
-
- tr.(*trace).reset()
-
- if !reflect.DeepEqual(tr, new(trace)) {
- t.Errorf("reset didn't clear all fields: %+v", tr)
- }
-}
-
-// TestResetLog checks whether all the fields are zeroed after reset.
-func TestResetLog(t *testing.T) {
- el := NewEventLog("foo", "bar")
- el.Printf("message")
- el.Errorf("error")
- el.Finish()
-
- el.(*eventLog).reset()
-
- if !reflect.DeepEqual(el, new(eventLog)) {
- t.Errorf("reset didn't clear all fields: %+v", el)
- }
-}
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/.travis.yml b/Godeps/_workspace/src/google.golang.org/grpc/.travis.yml
index 3f83776ec5f..055d6641c7a 100644
--- a/Godeps/_workspace/src/google.golang.org/grpc/.travis.yml
+++ b/Godeps/_workspace/src/google.golang.org/grpc/.travis.yml
@@ -11,4 +11,3 @@ install:
script:
- make test testrace
- - make coverage
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/benchmark/benchmark_test.go b/Godeps/_workspace/src/google.golang.org/grpc/benchmark/benchmark_test.go
deleted file mode 100644
index 9138ca6d966..00000000000
--- a/Godeps/_workspace/src/google.golang.org/grpc/benchmark/benchmark_test.go
+++ /dev/null
@@ -1,197 +0,0 @@
-package benchmark
-
-import (
- "os"
- "sync"
- "testing"
- "time"
-
- "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
- "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc"
- testpb "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/grpc_testing"
- "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/stats"
-)
-
-func runUnary(b *testing.B, maxConcurrentCalls int) {
- s := stats.AddStats(b, 38)
- b.StopTimer()
- target, stopper := StartServer("localhost:0")
- defer stopper()
- conn := NewClientConn(target)
- tc := testpb.NewTestServiceClient(conn)
-
- // Warm up connection.
- for i := 0; i < 10; i++ {
- unaryCaller(tc)
- }
- ch := make(chan int, maxConcurrentCalls*4)
- var (
- mu sync.Mutex
- wg sync.WaitGroup
- )
- wg.Add(maxConcurrentCalls)
-
- // Distribute the b.N calls over maxConcurrentCalls workers.
- for i := 0; i < maxConcurrentCalls; i++ {
- go func() {
- for range ch {
- start := time.Now()
- unaryCaller(tc)
- elapse := time.Since(start)
- mu.Lock()
- s.Add(elapse)
- mu.Unlock()
- }
- wg.Done()
- }()
- }
- b.StartTimer()
- for i := 0; i < b.N; i++ {
- ch <- i
- }
- b.StopTimer()
- close(ch)
- wg.Wait()
- conn.Close()
-}
-
-func runStream(b *testing.B, maxConcurrentCalls int) {
- s := stats.AddStats(b, 38)
- b.StopTimer()
- target, stopper := StartServer("localhost:0")
- defer stopper()
- conn := NewClientConn(target)
- tc := testpb.NewTestServiceClient(conn)
-
- // Warm up connection.
- stream, err := tc.StreamingCall(context.Background())
- if err != nil {
- b.Fatalf("%v.StreamingCall(_) = _, %v", tc, err)
- }
- for i := 0; i < 10; i++ {
- streamCaller(tc, stream)
- }
-
- ch := make(chan int, maxConcurrentCalls*4)
- var (
- mu sync.Mutex
- wg sync.WaitGroup
- )
- wg.Add(maxConcurrentCalls)
-
- // Distribute the b.N calls over maxConcurrentCalls workers.
- for i := 0; i < maxConcurrentCalls; i++ {
- go func() {
- stream, err := tc.StreamingCall(context.Background())
- if err != nil {
- b.Fatalf("%v.StreamingCall(_) = _, %v", tc, err)
- }
- for range ch {
- start := time.Now()
- streamCaller(tc, stream)
- elapse := time.Since(start)
- mu.Lock()
- s.Add(elapse)
- mu.Unlock()
- }
- wg.Done()
- }()
- }
- b.StartTimer()
- for i := 0; i < b.N; i++ {
- ch <- i
- }
- b.StopTimer()
- close(ch)
- wg.Wait()
- conn.Close()
-}
-func unaryCaller(client testpb.TestServiceClient) {
- DoUnaryCall(client, 1, 1)
-}
-
-func streamCaller(client testpb.TestServiceClient, stream testpb.TestService_StreamingCallClient) {
- DoStreamingRoundTrip(client, stream, 1, 1)
-}
-
-func BenchmarkClientStreamc1(b *testing.B) {
- grpc.EnableTracing = true
- runStream(b, 1)
-}
-
-func BenchmarkClientStreamc8(b *testing.B) {
- grpc.EnableTracing = true
- runStream(b, 8)
-}
-
-func BenchmarkClientStreamc64(b *testing.B) {
- grpc.EnableTracing = true
- runStream(b, 64)
-}
-
-func BenchmarkClientStreamc512(b *testing.B) {
- grpc.EnableTracing = true
- runStream(b, 512)
-}
-func BenchmarkClientUnaryc1(b *testing.B) {
- grpc.EnableTracing = true
- runUnary(b, 1)
-}
-
-func BenchmarkClientUnaryc8(b *testing.B) {
- grpc.EnableTracing = true
- runUnary(b, 8)
-}
-
-func BenchmarkClientUnaryc64(b *testing.B) {
- grpc.EnableTracing = true
- runUnary(b, 64)
-}
-
-func BenchmarkClientUnaryc512(b *testing.B) {
- grpc.EnableTracing = true
- runUnary(b, 512)
-}
-
-func BenchmarkClientStreamNoTracec1(b *testing.B) {
- grpc.EnableTracing = false
- runStream(b, 1)
-}
-
-func BenchmarkClientStreamNoTracec8(b *testing.B) {
- grpc.EnableTracing = false
- runStream(b, 8)
-}
-
-func BenchmarkClientStreamNoTracec64(b *testing.B) {
- grpc.EnableTracing = false
- runStream(b, 64)
-}
-
-func BenchmarkClientStreamNoTracec512(b *testing.B) {
- grpc.EnableTracing = false
- runStream(b, 512)
-}
-func BenchmarkClientUnaryNoTracec1(b *testing.B) {
- grpc.EnableTracing = false
- runUnary(b, 1)
-}
-
-func BenchmarkClientUnaryNoTracec8(b *testing.B) {
- grpc.EnableTracing = false
- runUnary(b, 8)
-}
-
-func BenchmarkClientUnaryNoTracec64(b *testing.B) {
- grpc.EnableTracing = false
- runUnary(b, 64)
-}
-
-func BenchmarkClientUnaryNoTracec512(b *testing.B) {
- grpc.EnableTracing = false
- runUnary(b, 512)
-}
-
-func TestMain(m *testing.M) {
- os.Exit(stats.RunTestMain(m))
-}
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/benchmark/grpc_testing/test.pb.go b/Godeps/_workspace/src/google.golang.org/grpc/benchmark/grpc_testing/test.pb.go
index 8bb3395a400..2bdc1a95859 100644
--- a/Godeps/_workspace/src/google.golang.org/grpc/benchmark/grpc_testing/test.pb.go
+++ b/Godeps/_workspace/src/google.golang.org/grpc/benchmark/grpc_testing/test.pb.go
@@ -27,18 +27,22 @@ It has these top-level messages:
package grpc_testing
import proto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+import fmt "fmt"
+import math "math"
import (
context "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
grpc "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc"
)
-// Reference imports to suppress errors if they are not otherwise used.
-var _ context.Context
-var _ grpc.ClientConn
-
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+const _ = proto.ProtoPackageIsVersion1
type PayloadType int32
@@ -65,6 +69,7 @@ var PayloadType_value = map[string]int32{
func (x PayloadType) String() string {
return proto.EnumName(PayloadType_name, int32(x))
}
+func (PayloadType) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
type ClientType int32
@@ -85,6 +90,7 @@ var ClientType_value = map[string]int32{
func (x ClientType) String() string {
return proto.EnumName(ClientType_name, int32(x))
}
+func (ClientType) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
type ServerType int32
@@ -105,6 +111,7 @@ var ServerType_value = map[string]int32{
func (x ServerType) String() string {
return proto.EnumName(ServerType_name, int32(x))
}
+func (ServerType) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
type RpcType int32
@@ -125,15 +132,17 @@ var RpcType_value = map[string]int32{
func (x RpcType) String() string {
return proto.EnumName(RpcType_name, int32(x))
}
+func (RpcType) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
type StatsRequest struct {
// run number
TestNum int32 `protobuf:"varint,1,opt,name=test_num" json:"test_num,omitempty"`
}
-func (m *StatsRequest) Reset() { *m = StatsRequest{} }
-func (m *StatsRequest) String() string { return proto.CompactTextString(m) }
-func (*StatsRequest) ProtoMessage() {}
+func (m *StatsRequest) Reset() { *m = StatsRequest{} }
+func (m *StatsRequest) String() string { return proto.CompactTextString(m) }
+func (*StatsRequest) ProtoMessage() {}
+func (*StatsRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
type ServerStats struct {
// wall clock time
@@ -144,9 +153,10 @@ type ServerStats struct {
TimeSystem float64 `protobuf:"fixed64,3,opt,name=time_system" json:"time_system,omitempty"`
}
-func (m *ServerStats) Reset() { *m = ServerStats{} }
-func (m *ServerStats) String() string { return proto.CompactTextString(m) }
-func (*ServerStats) ProtoMessage() {}
+func (m *ServerStats) Reset() { *m = ServerStats{} }
+func (m *ServerStats) String() string { return proto.CompactTextString(m) }
+func (*ServerStats) ProtoMessage() {}
+func (*ServerStats) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
type Payload struct {
// The type of data in body.
@@ -155,9 +165,10 @@ type Payload struct {
Body []byte `protobuf:"bytes,2,opt,name=body,proto3" json:"body,omitempty"`
}
-func (m *Payload) Reset() { *m = Payload{} }
-func (m *Payload) String() string { return proto.CompactTextString(m) }
-func (*Payload) ProtoMessage() {}
+func (m *Payload) Reset() { *m = Payload{} }
+func (m *Payload) String() string { return proto.CompactTextString(m) }
+func (*Payload) ProtoMessage() {}
+func (*Payload) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
type HistogramData struct {
Bucket []uint32 `protobuf:"varint,1,rep,name=bucket" json:"bucket,omitempty"`
@@ -168,9 +179,10 @@ type HistogramData struct {
Count float64 `protobuf:"fixed64,6,opt,name=count" json:"count,omitempty"`
}
-func (m *HistogramData) Reset() { *m = HistogramData{} }
-func (m *HistogramData) String() string { return proto.CompactTextString(m) }
-func (*HistogramData) ProtoMessage() {}
+func (m *HistogramData) Reset() { *m = HistogramData{} }
+func (m *HistogramData) String() string { return proto.CompactTextString(m) }
+func (*HistogramData) ProtoMessage() {}
+func (*HistogramData) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
type ClientConfig struct {
ServerTargets []string `protobuf:"bytes,1,rep,name=server_targets" json:"server_targets,omitempty"`
@@ -184,41 +196,141 @@ type ClientConfig struct {
RpcType RpcType `protobuf:"varint,8,opt,name=rpc_type,enum=grpc.testing.RpcType" json:"rpc_type,omitempty"`
}
-func (m *ClientConfig) Reset() { *m = ClientConfig{} }
-func (m *ClientConfig) String() string { return proto.CompactTextString(m) }
-func (*ClientConfig) ProtoMessage() {}
+func (m *ClientConfig) Reset() { *m = ClientConfig{} }
+func (m *ClientConfig) String() string { return proto.CompactTextString(m) }
+func (*ClientConfig) ProtoMessage() {}
+func (*ClientConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
// Request current stats
type Mark struct {
}
-func (m *Mark) Reset() { *m = Mark{} }
-func (m *Mark) String() string { return proto.CompactTextString(m) }
-func (*Mark) ProtoMessage() {}
+func (m *Mark) Reset() { *m = Mark{} }
+func (m *Mark) String() string { return proto.CompactTextString(m) }
+func (*Mark) ProtoMessage() {}
+func (*Mark) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
type ClientArgs struct {
- Setup *ClientConfig `protobuf:"bytes,1,opt,name=setup" json:"setup,omitempty"`
- Mark *Mark `protobuf:"bytes,2,opt,name=mark" json:"mark,omitempty"`
+ // Types that are valid to be assigned to Argtype:
+ // *ClientArgs_Setup
+ // *ClientArgs_Mark
+ Argtype isClientArgs_Argtype `protobuf_oneof:"argtype"`
}
-func (m *ClientArgs) Reset() { *m = ClientArgs{} }
-func (m *ClientArgs) String() string { return proto.CompactTextString(m) }
-func (*ClientArgs) ProtoMessage() {}
+func (m *ClientArgs) Reset() { *m = ClientArgs{} }
+func (m *ClientArgs) String() string { return proto.CompactTextString(m) }
+func (*ClientArgs) ProtoMessage() {}
+func (*ClientArgs) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
-func (m *ClientArgs) GetSetup() *ClientConfig {
+type isClientArgs_Argtype interface {
+ isClientArgs_Argtype()
+}
+
+type ClientArgs_Setup struct {
+ Setup *ClientConfig `protobuf:"bytes,1,opt,name=setup,oneof"`
+}
+type ClientArgs_Mark struct {
+ Mark *Mark `protobuf:"bytes,2,opt,name=mark,oneof"`
+}
+
+func (*ClientArgs_Setup) isClientArgs_Argtype() {}
+func (*ClientArgs_Mark) isClientArgs_Argtype() {}
+
+func (m *ClientArgs) GetArgtype() isClientArgs_Argtype {
if m != nil {
- return m.Setup
+ return m.Argtype
+ }
+ return nil
+}
+
+func (m *ClientArgs) GetSetup() *ClientConfig {
+ if x, ok := m.GetArgtype().(*ClientArgs_Setup); ok {
+ return x.Setup
}
return nil
}
func (m *ClientArgs) GetMark() *Mark {
- if m != nil {
- return m.Mark
+ if x, ok := m.GetArgtype().(*ClientArgs_Mark); ok {
+ return x.Mark
+ }
+ return nil
+}
+
+// XXX_OneofFuncs is for the internal use of the proto package.
+func (*ClientArgs) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) {
+ return _ClientArgs_OneofMarshaler, _ClientArgs_OneofUnmarshaler, _ClientArgs_OneofSizer, []interface{}{
+ (*ClientArgs_Setup)(nil),
+ (*ClientArgs_Mark)(nil),
+ }
+}
+
+func _ClientArgs_OneofMarshaler(msg proto.Message, b *proto.Buffer) error {
+ m := msg.(*ClientArgs)
+ // argtype
+ switch x := m.Argtype.(type) {
+ case *ClientArgs_Setup:
+ b.EncodeVarint(1<<3 | proto.WireBytes)
+ if err := b.EncodeMessage(x.Setup); err != nil {
+ return err
+ }
+ case *ClientArgs_Mark:
+ b.EncodeVarint(2<<3 | proto.WireBytes)
+ if err := b.EncodeMessage(x.Mark); err != nil {
+ return err
+ }
+ case nil:
+ default:
+ return fmt.Errorf("ClientArgs.Argtype has unexpected type %T", x)
}
return nil
}
+func _ClientArgs_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) {
+ m := msg.(*ClientArgs)
+ switch tag {
+ case 1: // argtype.setup
+ if wire != proto.WireBytes {
+ return true, proto.ErrInternalBadWireType
+ }
+ msg := new(ClientConfig)
+ err := b.DecodeMessage(msg)
+ m.Argtype = &ClientArgs_Setup{msg}
+ return true, err
+ case 2: // argtype.mark
+ if wire != proto.WireBytes {
+ return true, proto.ErrInternalBadWireType
+ }
+ msg := new(Mark)
+ err := b.DecodeMessage(msg)
+ m.Argtype = &ClientArgs_Mark{msg}
+ return true, err
+ default:
+ return false, nil
+ }
+}
+
+func _ClientArgs_OneofSizer(msg proto.Message) (n int) {
+ m := msg.(*ClientArgs)
+ // argtype
+ switch x := m.Argtype.(type) {
+ case *ClientArgs_Setup:
+ s := proto.Size(x.Setup)
+ n += proto.SizeVarint(1<<3 | proto.WireBytes)
+ n += proto.SizeVarint(uint64(s))
+ n += s
+ case *ClientArgs_Mark:
+ s := proto.Size(x.Mark)
+ n += proto.SizeVarint(2<<3 | proto.WireBytes)
+ n += proto.SizeVarint(uint64(s))
+ n += s
+ case nil:
+ default:
+ panic(fmt.Sprintf("proto: unexpected type %T in oneof", x))
+ }
+ return n
+}
+
type ClientStats struct {
Latencies *HistogramData `protobuf:"bytes,1,opt,name=latencies" json:"latencies,omitempty"`
TimeElapsed float64 `protobuf:"fixed64,3,opt,name=time_elapsed" json:"time_elapsed,omitempty"`
@@ -226,9 +338,10 @@ type ClientStats struct {
TimeSystem float64 `protobuf:"fixed64,5,opt,name=time_system" json:"time_system,omitempty"`
}
-func (m *ClientStats) Reset() { *m = ClientStats{} }
-func (m *ClientStats) String() string { return proto.CompactTextString(m) }
-func (*ClientStats) ProtoMessage() {}
+func (m *ClientStats) Reset() { *m = ClientStats{} }
+func (m *ClientStats) String() string { return proto.CompactTextString(m) }
+func (*ClientStats) ProtoMessage() {}
+func (*ClientStats) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} }
func (m *ClientStats) GetLatencies() *HistogramData {
if m != nil {
@@ -241,9 +354,10 @@ type ClientStatus struct {
Stats *ClientStats `protobuf:"bytes,1,opt,name=stats" json:"stats,omitempty"`
}
-func (m *ClientStatus) Reset() { *m = ClientStatus{} }
-func (m *ClientStatus) String() string { return proto.CompactTextString(m) }
-func (*ClientStatus) ProtoMessage() {}
+func (m *ClientStatus) Reset() { *m = ClientStatus{} }
+func (m *ClientStatus) String() string { return proto.CompactTextString(m) }
+func (*ClientStatus) ProtoMessage() {}
+func (*ClientStatus) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} }
func (m *ClientStatus) GetStats() *ClientStats {
if m != nil {
@@ -258,41 +372,141 @@ type ServerConfig struct {
EnableSsl bool `protobuf:"varint,3,opt,name=enable_ssl" json:"enable_ssl,omitempty"`
}
-func (m *ServerConfig) Reset() { *m = ServerConfig{} }
-func (m *ServerConfig) String() string { return proto.CompactTextString(m) }
-func (*ServerConfig) ProtoMessage() {}
+func (m *ServerConfig) Reset() { *m = ServerConfig{} }
+func (m *ServerConfig) String() string { return proto.CompactTextString(m) }
+func (*ServerConfig) ProtoMessage() {}
+func (*ServerConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} }
type ServerArgs struct {
- Setup *ServerConfig `protobuf:"bytes,1,opt,name=setup" json:"setup,omitempty"`
- Mark *Mark `protobuf:"bytes,2,opt,name=mark" json:"mark,omitempty"`
+ // Types that are valid to be assigned to Argtype:
+ // *ServerArgs_Setup
+ // *ServerArgs_Mark
+ Argtype isServerArgs_Argtype `protobuf_oneof:"argtype"`
}
-func (m *ServerArgs) Reset() { *m = ServerArgs{} }
-func (m *ServerArgs) String() string { return proto.CompactTextString(m) }
-func (*ServerArgs) ProtoMessage() {}
+func (m *ServerArgs) Reset() { *m = ServerArgs{} }
+func (m *ServerArgs) String() string { return proto.CompactTextString(m) }
+func (*ServerArgs) ProtoMessage() {}
+func (*ServerArgs) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} }
-func (m *ServerArgs) GetSetup() *ServerConfig {
+type isServerArgs_Argtype interface {
+ isServerArgs_Argtype()
+}
+
+type ServerArgs_Setup struct {
+ Setup *ServerConfig `protobuf:"bytes,1,opt,name=setup,oneof"`
+}
+type ServerArgs_Mark struct {
+ Mark *Mark `protobuf:"bytes,2,opt,name=mark,oneof"`
+}
+
+func (*ServerArgs_Setup) isServerArgs_Argtype() {}
+func (*ServerArgs_Mark) isServerArgs_Argtype() {}
+
+func (m *ServerArgs) GetArgtype() isServerArgs_Argtype {
if m != nil {
- return m.Setup
+ return m.Argtype
+ }
+ return nil
+}
+
+func (m *ServerArgs) GetSetup() *ServerConfig {
+ if x, ok := m.GetArgtype().(*ServerArgs_Setup); ok {
+ return x.Setup
}
return nil
}
func (m *ServerArgs) GetMark() *Mark {
- if m != nil {
- return m.Mark
+ if x, ok := m.GetArgtype().(*ServerArgs_Mark); ok {
+ return x.Mark
}
return nil
}
+// XXX_OneofFuncs is for the internal use of the proto package.
+func (*ServerArgs) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) {
+ return _ServerArgs_OneofMarshaler, _ServerArgs_OneofUnmarshaler, _ServerArgs_OneofSizer, []interface{}{
+ (*ServerArgs_Setup)(nil),
+ (*ServerArgs_Mark)(nil),
+ }
+}
+
+func _ServerArgs_OneofMarshaler(msg proto.Message, b *proto.Buffer) error {
+ m := msg.(*ServerArgs)
+ // argtype
+ switch x := m.Argtype.(type) {
+ case *ServerArgs_Setup:
+ b.EncodeVarint(1<<3 | proto.WireBytes)
+ if err := b.EncodeMessage(x.Setup); err != nil {
+ return err
+ }
+ case *ServerArgs_Mark:
+ b.EncodeVarint(2<<3 | proto.WireBytes)
+ if err := b.EncodeMessage(x.Mark); err != nil {
+ return err
+ }
+ case nil:
+ default:
+ return fmt.Errorf("ServerArgs.Argtype has unexpected type %T", x)
+ }
+ return nil
+}
+
+func _ServerArgs_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) {
+ m := msg.(*ServerArgs)
+ switch tag {
+ case 1: // argtype.setup
+ if wire != proto.WireBytes {
+ return true, proto.ErrInternalBadWireType
+ }
+ msg := new(ServerConfig)
+ err := b.DecodeMessage(msg)
+ m.Argtype = &ServerArgs_Setup{msg}
+ return true, err
+ case 2: // argtype.mark
+ if wire != proto.WireBytes {
+ return true, proto.ErrInternalBadWireType
+ }
+ msg := new(Mark)
+ err := b.DecodeMessage(msg)
+ m.Argtype = &ServerArgs_Mark{msg}
+ return true, err
+ default:
+ return false, nil
+ }
+}
+
+func _ServerArgs_OneofSizer(msg proto.Message) (n int) {
+ m := msg.(*ServerArgs)
+ // argtype
+ switch x := m.Argtype.(type) {
+ case *ServerArgs_Setup:
+ s := proto.Size(x.Setup)
+ n += proto.SizeVarint(1<<3 | proto.WireBytes)
+ n += proto.SizeVarint(uint64(s))
+ n += s
+ case *ServerArgs_Mark:
+ s := proto.Size(x.Mark)
+ n += proto.SizeVarint(2<<3 | proto.WireBytes)
+ n += proto.SizeVarint(uint64(s))
+ n += s
+ case nil:
+ default:
+ panic(fmt.Sprintf("proto: unexpected type %T in oneof", x))
+ }
+ return n
+}
+
type ServerStatus struct {
Stats *ServerStats `protobuf:"bytes,1,opt,name=stats" json:"stats,omitempty"`
Port int32 `protobuf:"varint,2,opt,name=port" json:"port,omitempty"`
}
-func (m *ServerStatus) Reset() { *m = ServerStatus{} }
-func (m *ServerStatus) String() string { return proto.CompactTextString(m) }
-func (*ServerStatus) ProtoMessage() {}
+func (m *ServerStatus) Reset() { *m = ServerStatus{} }
+func (m *ServerStatus) String() string { return proto.CompactTextString(m) }
+func (*ServerStatus) ProtoMessage() {}
+func (*ServerStatus) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} }
func (m *ServerStatus) GetStats() *ServerStats {
if m != nil {
@@ -312,9 +526,10 @@ type SimpleRequest struct {
Payload *Payload `protobuf:"bytes,3,opt,name=payload" json:"payload,omitempty"`
}
-func (m *SimpleRequest) Reset() { *m = SimpleRequest{} }
-func (m *SimpleRequest) String() string { return proto.CompactTextString(m) }
-func (*SimpleRequest) ProtoMessage() {}
+func (m *SimpleRequest) Reset() { *m = SimpleRequest{} }
+func (m *SimpleRequest) String() string { return proto.CompactTextString(m) }
+func (*SimpleRequest) ProtoMessage() {}
+func (*SimpleRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} }
func (m *SimpleRequest) GetPayload() *Payload {
if m != nil {
@@ -327,9 +542,10 @@ type SimpleResponse struct {
Payload *Payload `protobuf:"bytes,1,opt,name=payload" json:"payload,omitempty"`
}
-func (m *SimpleResponse) Reset() { *m = SimpleResponse{} }
-func (m *SimpleResponse) String() string { return proto.CompactTextString(m) }
-func (*SimpleResponse) ProtoMessage() {}
+func (m *SimpleResponse) Reset() { *m = SimpleResponse{} }
+func (m *SimpleResponse) String() string { return proto.CompactTextString(m) }
+func (*SimpleResponse) ProtoMessage() {}
+func (*SimpleResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} }
func (m *SimpleResponse) GetPayload() *Payload {
if m != nil {
@@ -339,12 +555,30 @@ func (m *SimpleResponse) GetPayload() *Payload {
}
func init() {
+ proto.RegisterType((*StatsRequest)(nil), "grpc.testing.StatsRequest")
+ proto.RegisterType((*ServerStats)(nil), "grpc.testing.ServerStats")
+ proto.RegisterType((*Payload)(nil), "grpc.testing.Payload")
+ proto.RegisterType((*HistogramData)(nil), "grpc.testing.HistogramData")
+ proto.RegisterType((*ClientConfig)(nil), "grpc.testing.ClientConfig")
+ proto.RegisterType((*Mark)(nil), "grpc.testing.Mark")
+ proto.RegisterType((*ClientArgs)(nil), "grpc.testing.ClientArgs")
+ proto.RegisterType((*ClientStats)(nil), "grpc.testing.ClientStats")
+ proto.RegisterType((*ClientStatus)(nil), "grpc.testing.ClientStatus")
+ proto.RegisterType((*ServerConfig)(nil), "grpc.testing.ServerConfig")
+ proto.RegisterType((*ServerArgs)(nil), "grpc.testing.ServerArgs")
+ proto.RegisterType((*ServerStatus)(nil), "grpc.testing.ServerStatus")
+ proto.RegisterType((*SimpleRequest)(nil), "grpc.testing.SimpleRequest")
+ proto.RegisterType((*SimpleResponse)(nil), "grpc.testing.SimpleResponse")
proto.RegisterEnum("grpc.testing.PayloadType", PayloadType_name, PayloadType_value)
proto.RegisterEnum("grpc.testing.ClientType", ClientType_name, ClientType_value)
proto.RegisterEnum("grpc.testing.ServerType", ServerType_name, ServerType_value)
proto.RegisterEnum("grpc.testing.RpcType", RpcType_name, RpcType_value)
}
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConn
+
// Client API for TestService service
type TestServiceClient interface {
@@ -639,3 +873,69 @@ var _Worker_serviceDesc = grpc.ServiceDesc{
},
},
}
+
+var fileDescriptor0 = []byte{
+ // 988 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xa4, 0x56, 0x5f, 0x6f, 0x1b, 0x45,
+ 0x10, 0xef, 0xc5, 0xff, 0xe2, 0x39, 0x27, 0x44, 0xab, 0x52, 0x39, 0x69, 0x11, 0x70, 0x05, 0x11,
+ 0x22, 0x91, 0x56, 0x46, 0x42, 0xea, 0x0b, 0x91, 0xeb, 0x1a, 0x52, 0x29, 0x71, 0xa2, 0xbd, 0x04,
+ 0xd4, 0xa7, 0xd3, 0xc6, 0xde, 0xb8, 0xa7, 0x9c, 0xef, 0xae, 0xb7, 0x7b, 0xa8, 0xe6, 0x09, 0xf1,
+ 0x19, 0xf8, 0x0a, 0x3c, 0x20, 0xbe, 0x24, 0xb3, 0xb3, 0x7b, 0x89, 0x9d, 0x9a, 0x36, 0x52, 0x9f,
+ 0x72, 0x3b, 0xf3, 0x9b, 0xdf, 0xce, 0xfe, 0xe6, 0x8f, 0x03, 0xa0, 0xa5, 0xd2, 0xfb, 0x79, 0x91,
+ 0xe9, 0x8c, 0x75, 0xa6, 0x45, 0x3e, 0xde, 0x37, 0x86, 0x38, 0x9d, 0x06, 0xdf, 0x42, 0x27, 0xd4,
+ 0x42, 0x2b, 0x2e, 0xdf, 0x94, 0x68, 0x62, 0xdb, 0xb0, 0x6e, 0x5c, 0x51, 0x5a, 0xce, 0xba, 0xde,
+ 0x17, 0xde, 0x6e, 0x83, 0xb7, 0xcc, 0x79, 0x54, 0xce, 0x82, 0x14, 0xfc, 0x50, 0x16, 0xbf, 0xc9,
+ 0x82, 0x02, 0xd8, 0x97, 0xd0, 0xd1, 0xf1, 0x4c, 0x46, 0x32, 0x11, 0xb9, 0x92, 0x13, 0x42, 0x7b,
+ 0xdc, 0x37, 0xb6, 0xa1, 0x35, 0xb1, 0x87, 0xd0, 0x26, 0x48, 0xa9, 0x64, 0xd1, 0x5d, 0x23, 0xff,
+ 0xba, 0x31, 0x9c, 0xe3, 0x99, 0x7d, 0x0e, 0x84, 0x8d, 0xd4, 0x5c, 0x69, 0x39, 0xeb, 0xd6, 0xc8,
+ 0x0d, 0xc6, 0x14, 0x92, 0x25, 0x38, 0x82, 0xd6, 0xa9, 0x98, 0x27, 0x99, 0x98, 0xb0, 0xef, 0xa0,
+ 0xae, 0xe7, 0xb9, 0xa4, 0x3b, 0x36, 0x7b, 0xdb, 0xfb, 0x8b, 0x4f, 0xd8, 0x77, 0xa0, 0x33, 0x04,
+ 0x70, 0x82, 0x31, 0x06, 0xf5, 0x8b, 0x6c, 0x32, 0xa7, 0x2b, 0x3b, 0x9c, 0xbe, 0x83, 0x7f, 0x3d,
+ 0xd8, 0x38, 0x8c, 0x95, 0xce, 0xa6, 0x85, 0x98, 0xbd, 0x10, 0x5a, 0xb0, 0x07, 0xd0, 0xbc, 0x28,
+ 0xc7, 0x57, 0x52, 0x23, 0x6d, 0x6d, 0x77, 0x83, 0xbb, 0x93, 0x91, 0x60, 0x16, 0xa7, 0x91, 0x92,
+ 0x32, 0x75, 0x49, 0xb7, 0xf0, 0x1c, 0xe2, 0x91, 0x5c, 0xe2, 0xad, 0x75, 0xd5, 0x9c, 0x4b, 0xbc,
+ 0x25, 0xd7, 0x16, 0xd4, 0x14, 0x6a, 0x56, 0x27, 0xab, 0xf9, 0x64, 0x5f, 0xc1, 0x26, 0xfe, 0x89,
+ 0xb2, 0xcb, 0x48, 0xbd, 0x29, 0x45, 0x21, 0x55, 0xb7, 0x41, 0xce, 0x0e, 0x5a, 0x4f, 0x2e, 0x43,
+ 0x6b, 0x63, 0xf7, 0xa1, 0x31, 0xce, 0xca, 0x54, 0x77, 0x9b, 0xe4, 0xb4, 0x87, 0xe0, 0x8f, 0x1a,
+ 0x74, 0x06, 0x49, 0x2c, 0x53, 0x3d, 0xc8, 0xd2, 0xcb, 0x78, 0xca, 0xbe, 0x46, 0x32, 0x12, 0x3f,
+ 0xd2, 0xa2, 0x98, 0x4a, 0xad, 0x28, 0xe9, 0x36, 0xdf, 0xb0, 0xd6, 0x33, 0x6b, 0x64, 0xcf, 0xc0,
+ 0x1f, 0x53, 0x58, 0x44, 0x7a, 0xad, 0x91, 0x5e, 0xdd, 0x65, 0xbd, 0x2c, 0x2f, 0xc9, 0x05, 0xe3,
+ 0xeb, 0x6f, 0xf6, 0x19, 0x80, 0x4c, 0xc5, 0x45, 0x82, 0x15, 0x51, 0x09, 0xbd, 0x6e, 0x9d, 0xb7,
+ 0xad, 0x25, 0x54, 0x09, 0x3b, 0x80, 0x47, 0x59, 0xa9, 0x95, 0x16, 0xe9, 0x04, 0x49, 0x22, 0x24,
+ 0x54, 0x51, 0x8e, 0xe9, 0x8c, 0x5f, 0x8b, 0x34, 0x95, 0x09, 0x3d, 0xbc, 0xc1, 0xb7, 0x17, 0x30,
+ 0x1c, 0x21, 0xa7, 0xb2, 0x18, 0x58, 0x00, 0xfb, 0x06, 0x3e, 0x71, 0xa9, 0xb9, 0x10, 0xab, 0x47,
+ 0x83, 0x6f, 0x5a, 0xb3, 0xc3, 0x51, 0x63, 0xe5, 0xb6, 0xa4, 0x91, 0x8a, 0x7f, 0x97, 0x24, 0x4c,
+ 0x83, 0xfb, 0xce, 0x16, 0xa2, 0x89, 0x3d, 0x85, 0xfb, 0x42, 0xcd, 0xd3, 0x71, 0x54, 0x3d, 0xf6,
+ 0x75, 0x21, 0xc5, 0x44, 0x75, 0x5b, 0x04, 0x65, 0xe4, 0x73, 0xcf, 0xb4, 0x1e, 0x8c, 0x58, 0xc7,
+ 0x94, 0xad, 0x2a, 0xeb, 0xa4, 0xca, 0xa7, 0xcb, 0xaa, 0x60, 0xb6, 0x24, 0x49, 0xab, 0xb0, 0x1f,
+ 0x41, 0x13, 0xea, 0xc7, 0xa2, 0xb8, 0x0a, 0x4a, 0x00, 0x4b, 0xd5, 0x2f, 0xa6, 0x8a, 0xf5, 0xa0,
+ 0xa1, 0xa4, 0x2e, 0x73, 0x6a, 0x45, 0xbf, 0xb7, 0xb3, 0x4a, 0x5a, 0x5b, 0xb2, 0xc3, 0x7b, 0xdc,
+ 0x42, 0xd9, 0x2e, 0xd4, 0x67, 0xc8, 0x44, 0xd5, 0xf0, 0x7b, 0x6c, 0x39, 0xc4, 0xdc, 0x81, 0x50,
+ 0x42, 0x3c, 0x6f, 0x43, 0x0b, 0x0b, 0x69, 0x92, 0x0c, 0xfe, 0xf1, 0xc0, 0xb7, 0x74, 0x76, 0xdc,
+ 0x9e, 0x41, 0x3b, 0x11, 0x5a, 0xa6, 0xe3, 0x58, 0x2a, 0x77, 0xf9, 0xc3, 0x65, 0xa6, 0xa5, 0xee,
+ 0xe6, 0x37, 0xe8, 0x77, 0x26, 0xb5, 0xf6, 0x81, 0x49, 0xad, 0xbf, 0x7f, 0x52, 0x1b, 0xef, 0x4c,
+ 0xea, 0x41, 0xd5, 0xac, 0x26, 0xd5, 0x52, 0xb1, 0x27, 0x28, 0x92, 0x49, 0xda, 0xe5, 0xb9, 0xbd,
+ 0x4a, 0x24, 0xbb, 0x75, 0x2c, 0x2e, 0xf8, 0xd3, 0xc3, 0x35, 0x44, 0x8d, 0xec, 0xda, 0x1d, 0xfb,
+ 0xb8, 0x6a, 0xf7, 0x9b, 0xb9, 0xbf, 0xd5, 0xc7, 0x36, 0xc0, 0xf6, 0xb1, 0xba, 0xfe, 0x66, 0x5d,
+ 0x68, 0x55, 0xed, 0xb0, 0xe6, 0x16, 0x98, 0xeb, 0x81, 0xf7, 0x77, 0xb8, 0x29, 0xb4, 0xa5, 0xbc,
+ 0x43, 0xa1, 0x17, 0x93, 0xfd, 0xc8, 0x42, 0x87, 0xd5, 0xd3, 0xef, 0x24, 0xde, 0xc2, 0x06, 0x76,
+ 0xe2, 0x99, 0x6d, 0x97, 0x67, 0x85, 0x76, 0xaf, 0xa5, 0xef, 0xe0, 0x6f, 0xdc, 0x76, 0x61, 0x3c,
+ 0xcb, 0x13, 0x59, 0x2d, 0xf6, 0x1f, 0x61, 0x03, 0xd7, 0x4d, 0x9e, 0xa5, 0x4a, 0x46, 0x77, 0xdb,
+ 0xa5, 0x9d, 0x0a, 0x4f, 0xb2, 0x3e, 0x5e, 0x88, 0xa7, 0xb1, 0xb4, 0xd7, 0x5d, 0x83, 0x68, 0x2e,
+ 0x9f, 0x40, 0xcb, 0x8d, 0x29, 0xc9, 0xeb, 0xdf, 0x1e, 0x32, 0x47, 0xcf, 0x2b, 0x54, 0xd0, 0x87,
+ 0xcd, 0x2a, 0x4d, 0x4b, 0xb3, 0x48, 0xe1, 0xdd, 0x85, 0x62, 0xef, 0x00, 0xfc, 0x85, 0xac, 0x71,
+ 0x0f, 0x77, 0x06, 0x27, 0xc7, 0xa7, 0x7c, 0x18, 0x86, 0xfd, 0xe7, 0x47, 0xc3, 0xad, 0x7b, 0xa8,
+ 0xcf, 0xe6, 0xf9, 0x68, 0xc9, 0xe6, 0x31, 0x80, 0x26, 0xef, 0x8f, 0x5e, 0x9c, 0x1c, 0x6f, 0xad,
+ 0xed, 0xfd, 0x50, 0x0d, 0x38, 0xc5, 0x3f, 0x00, 0x16, 0xbe, 0x1a, 0x0d, 0x0e, 0xf9, 0xc9, 0xe8,
+ 0xe4, 0x3c, 0x8c, 0x06, 0x47, 0x2f, 0x87, 0xa3, 0x33, 0x64, 0x41, 0xde, 0xbe, 0x71, 0x54, 0x16,
+ 0xcf, 0xc4, 0xdd, 0xb4, 0xe0, 0xed, 0xb8, 0x70, 0xc8, 0x7f, 0x19, 0xf2, 0xc5, 0x38, 0x67, 0xf1,
+ 0xf6, 0x1e, 0x43, 0xcb, 0x2d, 0x1b, 0xd6, 0x86, 0xc6, 0xf9, 0xa8, 0xcf, 0x5f, 0x21, 0x6e, 0x03,
+ 0xda, 0xe1, 0x19, 0x1f, 0xf6, 0x8f, 0x5f, 0x8e, 0x7e, 0xde, 0xf2, 0x7a, 0x58, 0x40, 0xff, 0x0c,
+ 0x9f, 0x6c, 0x6e, 0x88, 0xc7, 0x92, 0xfd, 0x04, 0xed, 0xf3, 0x54, 0x14, 0xf3, 0x81, 0x48, 0x12,
+ 0x76, 0x6b, 0xf0, 0x97, 0x0a, 0xbd, 0xf3, 0x68, 0xb5, 0xd3, 0xc9, 0x3b, 0xc2, 0xbe, 0xd0, 0x38,
+ 0x0e, 0xf8, 0x8b, 0x36, 0xfd, 0x48, 0xae, 0x5d, 0xef, 0xa9, 0xd7, 0xfb, 0xcb, 0x83, 0xe6, 0xaf,
+ 0x59, 0x71, 0x85, 0x6b, 0x62, 0x80, 0xef, 0x2a, 0x53, 0x93, 0x34, 0x5b, 0xf9, 0x8b, 0x63, 0xc6,
+ 0x6a, 0x67, 0xe7, 0xff, 0x76, 0x41, 0xa9, 0x0c, 0x1f, 0x1b, 0x42, 0x1b, 0x49, 0xac, 0xae, 0x6c,
+ 0xe5, 0xc0, 0xaf, 0xa2, 0x59, 0x1c, 0x20, 0x43, 0x73, 0xd1, 0xa4, 0xff, 0x75, 0xbe, 0xff, 0x2f,
+ 0x00, 0x00, 0xff, 0xff, 0xe3, 0xb1, 0x00, 0x4d, 0xf9, 0x08, 0x00, 0x00,
+}
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/benchmark/grpc_testing/test.proto b/Godeps/_workspace/src/google.golang.org/grpc/benchmark/grpc_testing/test.proto
index e3a27f861b8..b0b2f80b56f 100644
--- a/Godeps/_workspace/src/google.golang.org/grpc/benchmark/grpc_testing/test.proto
+++ b/Godeps/_workspace/src/google.golang.org/grpc/benchmark/grpc_testing/test.proto
@@ -17,7 +17,7 @@ enum PayloadType {
message StatsRequest {
// run number
- optional int32 test_num = 1;
+ int32 test_num = 1;
}
message ServerStats {
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/call.go b/Godeps/_workspace/src/google.golang.org/grpc/call.go
index 4a1f409f997..dba3aaf930c 100644
--- a/Godeps/_workspace/src/google.golang.org/grpc/call.go
+++ b/Godeps/_workspace/src/google.golang.org/grpc/call.go
@@ -55,9 +55,9 @@ func recvResponse(dopts dialOptions, t transport.ClientTransport, c *callInfo, s
if err != nil {
return err
}
- p := &parser{s: stream}
+ p := &parser{r: stream}
for {
- if err = recv(p, dopts.codec, stream, dopts.dg, reply); err != nil {
+ if err = recv(p, dopts.codec, stream, dopts.dc, reply); err != nil {
if err == io.EOF {
break
}
@@ -133,11 +133,7 @@ func Invoke(ctx context.Context, method string, args, reply interface{}, cc *Cli
}
var (
lastErr error // record the error that happened
- cp Compressor
)
- if cc.dopts.cg != nil {
- cp = cc.dopts.cg()
- }
for {
var (
err error
@@ -152,8 +148,8 @@ func Invoke(ctx context.Context, method string, args, reply interface{}, cc *Cli
Host: cc.authority,
Method: method,
}
- if cp != nil {
- callHdr.SendCompress = cp.Type()
+ if cc.dopts.cp != nil {
+ callHdr.SendCompress = cc.dopts.cp.Type()
}
t, err = cc.dopts.picker.Pick(ctx)
if err != nil {
@@ -166,7 +162,7 @@ func Invoke(ctx context.Context, method string, args, reply interface{}, cc *Cli
if c.traceInfo.tr != nil {
c.traceInfo.tr.LazyLog(&payload{sent: true, msg: args}, true)
}
- stream, err = sendRequest(ctx, cc.dopts.codec, cp, callHdr, t, args, topts)
+ stream, err = sendRequest(ctx, cc.dopts.codec, cc.dopts.cp, callHdr, t, args, topts)
if err != nil {
if _, ok := err.(transport.ConnectionError); ok {
lastErr = err
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/call_test.go b/Godeps/_workspace/src/google.golang.org/grpc/call_test.go
deleted file mode 100644
index 4371db4f1c5..00000000000
--- a/Godeps/_workspace/src/google.golang.org/grpc/call_test.go
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- *
- * Copyright 2014, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-package grpc
-
-import (
- "io"
- "math"
- "net"
- "strconv"
- "sync"
- "testing"
- "time"
-
- "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
- "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codes"
- "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport"
-)
-
-var (
- expectedRequest = "ping"
- expectedResponse = "pong"
- sizeLargeErr = 1024 * 1024
-)
-
-type testCodec struct {
-}
-
-func (testCodec) Marshal(v interface{}) ([]byte, error) {
- return []byte(*(v.(*string))), nil
-}
-
-func (testCodec) Unmarshal(data []byte, v interface{}) error {
- *(v.(*string)) = string(data)
- return nil
-}
-
-func (testCodec) String() string {
- return "test"
-}
-
-type testStreamHandler struct {
- t transport.ServerTransport
-}
-
-func (h *testStreamHandler) handleStream(t *testing.T, s *transport.Stream) {
- p := &parser{s: s}
- for {
- pf, req, err := p.recvMsg()
- if err == io.EOF {
- break
- }
- if err != nil {
- return
- }
- if pf != compressionNone {
- t.Errorf("Received the mistaken message format %d, want %d", pf, compressionNone)
- return
- }
- var v string
- codec := testCodec{}
- if err := codec.Unmarshal(req, &v); err != nil {
- t.Fatalf("Failed to unmarshal the received message %v", err)
- }
- if v != expectedRequest {
- h.t.WriteStatus(s, codes.Internal, string(make([]byte, sizeLargeErr)))
- return
- }
- }
- // send a response back to end the stream.
- reply, err := encode(testCodec{}, &expectedResponse, nil, nil)
- if err != nil {
- t.Fatalf("Failed to encode the response: %v", err)
- }
- h.t.Write(s, reply, &transport.Options{})
- h.t.WriteStatus(s, codes.OK, "")
-}
-
-type server struct {
- lis net.Listener
- port string
- // channel to signal server is ready to serve.
- readyChan chan bool
- mu sync.Mutex
- conns map[transport.ServerTransport]bool
-}
-
-// start starts server. Other goroutines should block on s.readyChan for futher operations.
-func (s *server) start(t *testing.T, port int, maxStreams uint32) {
- var err error
- if port == 0 {
- s.lis, err = net.Listen("tcp", ":0")
- } else {
- s.lis, err = net.Listen("tcp", ":"+strconv.Itoa(port))
- }
- if err != nil {
- t.Fatalf("failed to listen: %v", err)
- }
- _, p, err := net.SplitHostPort(s.lis.Addr().String())
- if err != nil {
- t.Fatalf("failed to parse listener address: %v", err)
- }
- s.port = p
- s.conns = make(map[transport.ServerTransport]bool)
- if s.readyChan != nil {
- close(s.readyChan)
- }
- for {
- conn, err := s.lis.Accept()
- if err != nil {
- return
- }
- st, err := transport.NewServerTransport("http2", conn, maxStreams, nil)
- if err != nil {
- return
- }
- s.mu.Lock()
- if s.conns == nil {
- s.mu.Unlock()
- st.Close()
- return
- }
- s.conns[st] = true
- s.mu.Unlock()
- h := &testStreamHandler{st}
- go st.HandleStreams(func(s *transport.Stream) {
- go h.handleStream(t, s)
- })
- }
-}
-
-func (s *server) wait(t *testing.T, timeout time.Duration) {
- select {
- case <-s.readyChan:
- case <-time.After(timeout):
- t.Fatalf("Timed out after %v waiting for server to be ready", timeout)
- }
-}
-
-func (s *server) stop() {
- s.lis.Close()
- s.mu.Lock()
- for c := range s.conns {
- c.Close()
- }
- s.conns = nil
- s.mu.Unlock()
-}
-
-func setUp(t *testing.T, port int, maxStreams uint32) (*server, *ClientConn) {
- server := &server{readyChan: make(chan bool)}
- go server.start(t, port, maxStreams)
- server.wait(t, 2*time.Second)
- addr := "localhost:" + server.port
- cc, err := Dial(addr, WithBlock(), WithInsecure(), WithCodec(testCodec{}))
- if err != nil {
- t.Fatalf("Failed to create ClientConn: %v", err)
- }
- return server, cc
-}
-
-func TestInvoke(t *testing.T) {
- server, cc := setUp(t, 0, math.MaxUint32)
- var reply string
- if err := Invoke(context.Background(), "/foo/bar", &expectedRequest, &reply, cc); err != nil || reply != expectedResponse {
- t.Fatalf("grpc.Invoke(_, _, _, _, _) = %v, want ", err)
- }
- cc.Close()
- server.stop()
-}
-
-func TestInvokeLargeErr(t *testing.T) {
- server, cc := setUp(t, 0, math.MaxUint32)
- var reply string
- req := "hello"
- err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc)
- if _, ok := err.(rpcError); !ok {
- t.Fatalf("grpc.Invoke(_, _, _, _, _) receives non rpc error.")
- }
- if Code(err) != codes.Internal || len(ErrorDesc(err)) != sizeLargeErr {
- t.Fatalf("grpc.Invoke(_, _, _, _, _) = %v, want an error of code %d and desc size %d", err, codes.Internal, sizeLargeErr)
- }
- cc.Close()
- server.stop()
-}
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/clientconn.go b/Godeps/_workspace/src/google.golang.org/grpc/clientconn.go
index 3cd89c255b7..2269a6e4c0f 100644
--- a/Godeps/_workspace/src/google.golang.org/grpc/clientconn.go
+++ b/Godeps/_workspace/src/google.golang.org/grpc/clientconn.go
@@ -73,8 +73,8 @@ var (
// values passed to Dial.
type dialOptions struct {
codec Codec
- cg CompressorGenerator
- dg DecompressorGenerator
+ cp Compressor
+ dc Decompressor
picker Picker
block bool
insecure bool
@@ -93,17 +93,17 @@ func WithCodec(c Codec) DialOption {
// WithCompressor returns a DialOption which sets a CompressorGenerator for generating message
// compressor.
-func WithCompressor(f CompressorGenerator) DialOption {
+func WithCompressor(cp Compressor) DialOption {
return func(o *dialOptions) {
- o.cg = f
+ o.cp = cp
}
}
// WithDecompressor returns a DialOption which sets a DecompressorGenerator for generating
// message decompressor.
-func WithDecompressor(f DecompressorGenerator) DialOption {
+func WithDecompressor(dc Decompressor) DialOption {
return func(o *dialOptions) {
- o.dg = f
+ o.dc = dc
}
}
@@ -539,8 +539,9 @@ func (cc *Conn) Wait(ctx context.Context) (transport.ClientTransport, error) {
cc.mu.Unlock()
return nil, ErrClientConnClosing
case cc.state == Ready:
+ ct := cc.transport
cc.mu.Unlock()
- return cc.transport, nil
+ return ct, nil
default:
ready := cc.ready
if ready == nil {
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/clientconn_test.go b/Godeps/_workspace/src/google.golang.org/grpc/clientconn_test.go
deleted file mode 100644
index b5b651822ba..00000000000
--- a/Godeps/_workspace/src/google.golang.org/grpc/clientconn_test.go
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- *
- * Copyright 2014, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-package grpc
-
-import (
- "testing"
- "time"
-
- "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/credentials"
-)
-
-const tlsDir = "testdata/"
-
-func TestDialTimeout(t *testing.T) {
- conn, err := Dial("Non-Existent.Server:80", WithTimeout(time.Millisecond), WithBlock(), WithInsecure())
- if err == nil {
- conn.Close()
- }
- if err != ErrClientConnTimeout {
- t.Fatalf("Dial(_, _) = %v, %v, want %v", conn, err, ErrClientConnTimeout)
- }
-}
-
-func TestTLSDialTimeout(t *testing.T) {
- creds, err := credentials.NewClientTLSFromFile(tlsDir+"ca.pem", "x.test.youtube.com")
- if err != nil {
- t.Fatalf("Failed to create credentials %v", err)
- }
- conn, err := Dial("Non-Existent.Server:80", WithTransportCredentials(creds), WithTimeout(time.Millisecond), WithBlock())
- if err == nil {
- conn.Close()
- }
- if err != ErrClientConnTimeout {
- t.Fatalf("grpc.Dial(_, _) = %v, %v, want %v", conn, err, ErrClientConnTimeout)
- }
-}
-
-func TestCredentialsMisuse(t *testing.T) {
- creds, err := credentials.NewClientTLSFromFile(tlsDir+"ca.pem", "x.test.youtube.com")
- if err != nil {
- t.Fatalf("Failed to create credentials %v", err)
- }
- // Two conflicting credential configurations
- if _, err := Dial("Non-Existent.Server:80", WithTransportCredentials(creds), WithTimeout(time.Millisecond), WithBlock(), WithInsecure()); err != ErrCredentialsMisuse {
- t.Fatalf("Dial(_, _) = _, %v, want _, %v", err, ErrCredentialsMisuse)
- }
- // security info on insecure connection
- if _, err := Dial("Non-Existent.Server:80", WithPerRPCCredentials(creds), WithTimeout(time.Millisecond), WithBlock(), WithInsecure()); err != ErrCredentialsMisuse {
- t.Fatalf("Dial(_, _) = _, %v, want _, %v", err, ErrCredentialsMisuse)
- }
-}
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/doc.go b/Godeps/_workspace/src/google.golang.org/grpc/doc.go
index c0f54f75943..b4c0e740e9c 100644
--- a/Godeps/_workspace/src/google.golang.org/grpc/doc.go
+++ b/Godeps/_workspace/src/google.golang.org/grpc/doc.go
@@ -1,6 +1,6 @@
/*
Package grpc implements an RPC system called gRPC.
-See https://github.com/grpc/grpc for more information about gRPC.
+See www.grpc.io for more information about gRPC.
*/
package grpc
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/examples/helloworld/helloworld/helloworld.pb.go b/Godeps/_workspace/src/google.golang.org/grpc/examples/helloworld/helloworld/helloworld.pb.go
index 2a0abb251c1..38715041b42 100644
--- a/Godeps/_workspace/src/google.golang.org/grpc/examples/helloworld/helloworld/helloworld.pb.go
+++ b/Godeps/_workspace/src/google.golang.org/grpc/examples/helloworld/helloworld/helloworld.pb.go
@@ -15,40 +15,52 @@ It has these top-level messages:
package helloworld
import proto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+import fmt "fmt"
+import math "math"
import (
context "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
grpc "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc"
)
-// Reference imports to suppress errors if they are not otherwise used.
-var _ context.Context
-var _ grpc.ClientConn
-
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+const _ = proto.ProtoPackageIsVersion1
// The request message containing the user's name.
type HelloRequest struct {
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
}
-func (m *HelloRequest) Reset() { *m = HelloRequest{} }
-func (m *HelloRequest) String() string { return proto.CompactTextString(m) }
-func (*HelloRequest) ProtoMessage() {}
+func (m *HelloRequest) Reset() { *m = HelloRequest{} }
+func (m *HelloRequest) String() string { return proto.CompactTextString(m) }
+func (*HelloRequest) ProtoMessage() {}
+func (*HelloRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
// The response message containing the greetings
type HelloReply struct {
Message string `protobuf:"bytes,1,opt,name=message" json:"message,omitempty"`
}
-func (m *HelloReply) Reset() { *m = HelloReply{} }
-func (m *HelloReply) String() string { return proto.CompactTextString(m) }
-func (*HelloReply) ProtoMessage() {}
+func (m *HelloReply) Reset() { *m = HelloReply{} }
+func (m *HelloReply) String() string { return proto.CompactTextString(m) }
+func (*HelloReply) ProtoMessage() {}
+func (*HelloReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func init() {
+ proto.RegisterType((*HelloRequest)(nil), "helloworld.HelloRequest")
+ proto.RegisterType((*HelloReply)(nil), "helloworld.HelloReply")
}
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConn
+
// Client API for Greeter service
type GreeterClient interface {
@@ -107,3 +119,19 @@ var _Greeter_serviceDesc = grpc.ServiceDesc{
},
Streams: []grpc.StreamDesc{},
}
+
+var fileDescriptor0 = []byte{
+ // 181 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x12, 0xc8, 0x48, 0xcd, 0xc9,
+ 0xc9, 0x2f, 0xcf, 0x2f, 0xca, 0x49, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x42, 0x88,
+ 0x28, 0x29, 0x71, 0xf1, 0x78, 0x80, 0x78, 0x41, 0xa9, 0x85, 0xa5, 0xa9, 0xc5, 0x25, 0x42, 0x42,
+ 0x5c, 0x2c, 0x79, 0x89, 0xb9, 0xa9, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0x60, 0xb6, 0x92,
+ 0x1a, 0x17, 0x17, 0x54, 0x4d, 0x41, 0x4e, 0xa5, 0x90, 0x04, 0x17, 0x7b, 0x6e, 0x6a, 0x71, 0x71,
+ 0x62, 0x3a, 0x4c, 0x11, 0x8c, 0x6b, 0xe4, 0xc9, 0xc5, 0xee, 0x5e, 0x94, 0x9a, 0x5a, 0x92, 0x5a,
+ 0x24, 0x64, 0xc7, 0xc5, 0x11, 0x9c, 0x58, 0x09, 0xd6, 0x25, 0x24, 0xa1, 0x87, 0xe4, 0x02, 0x64,
+ 0xcb, 0xa4, 0xc4, 0xb0, 0xc8, 0x00, 0xad, 0x50, 0x62, 0x70, 0x32, 0xe3, 0x92, 0xce, 0xcc, 0xd7,
+ 0x4b, 0x2f, 0x2a, 0x48, 0xd6, 0x4b, 0xad, 0x48, 0xcc, 0x2d, 0xc8, 0x49, 0x2d, 0x46, 0x52, 0xeb,
+ 0xc4, 0x0f, 0x56, 0x1c, 0x0e, 0x62, 0x07, 0x80, 0xbc, 0x14, 0xc0, 0xb8, 0x88, 0x89, 0xd9, 0xc3,
+ 0x27, 0x3c, 0x89, 0x0d, 0xec, 0x43, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xdf, 0x0a, 0xdc,
+ 0xe8, 0xf5, 0x00, 0x00, 0x00,
+}
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/examples/helloworld/helloworld/helloworld.proto b/Godeps/_workspace/src/google.golang.org/grpc/examples/helloworld/helloworld/helloworld.proto
index 7d58870a708..0bee1fcfcfb 100644
--- a/Godeps/_workspace/src/google.golang.org/grpc/examples/helloworld/helloworld/helloworld.proto
+++ b/Godeps/_workspace/src/google.golang.org/grpc/examples/helloworld/helloworld/helloworld.proto
@@ -29,7 +29,9 @@
syntax = "proto3";
-option java_package = "io.grpc.examples";
+option java_multiple_files = true;
+option java_package = "io.grpc.examples.helloworld";
+option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";
package helloworld;
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/routeguide/route_guide.pb.go b/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/routeguide/route_guide.pb.go
index 07ac22f9ee5..88a78b47b3a 100644
--- a/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/routeguide/route_guide.pb.go
+++ b/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/routeguide/route_guide.pb.go
@@ -1,12 +1,12 @@
// Code generated by protoc-gen-go.
-// source: google.golang.org/grpc/examples/route_guide/routeguide/route_guide.proto
+// source: route_guide.proto
// DO NOT EDIT!
/*
Package routeguide is a generated protocol buffer package.
It is generated from these files:
- google.golang.org/grpc/examples/route_guide/routeguide/route_guide.proto
+ route_guide.proto
It has these top-level messages:
Point
@@ -31,6 +31,10 @@ var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+const _ = proto.ProtoPackageIsVersion1
+
// Points are represented as latitude-longitude pairs in the E7 representation
// (degrees multiplied by 10**7 and rounded to the nearest integer).
// Latitudes should be in the range +/- 90 degrees and longitude should be in
@@ -40,9 +44,10 @@ type Point struct {
Longitude int32 `protobuf:"varint,2,opt,name=longitude" json:"longitude,omitempty"`
}
-func (m *Point) Reset() { *m = Point{} }
-func (m *Point) String() string { return proto.CompactTextString(m) }
-func (*Point) ProtoMessage() {}
+func (m *Point) Reset() { *m = Point{} }
+func (m *Point) String() string { return proto.CompactTextString(m) }
+func (*Point) ProtoMessage() {}
+func (*Point) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
// A latitude-longitude rectangle, represented as two diagonally opposite
// points "lo" and "hi".
@@ -53,9 +58,10 @@ type Rectangle struct {
Hi *Point `protobuf:"bytes,2,opt,name=hi" json:"hi,omitempty"`
}
-func (m *Rectangle) Reset() { *m = Rectangle{} }
-func (m *Rectangle) String() string { return proto.CompactTextString(m) }
-func (*Rectangle) ProtoMessage() {}
+func (m *Rectangle) Reset() { *m = Rectangle{} }
+func (m *Rectangle) String() string { return proto.CompactTextString(m) }
+func (*Rectangle) ProtoMessage() {}
+func (*Rectangle) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func (m *Rectangle) GetLo() *Point {
if m != nil {
@@ -81,9 +87,10 @@ type Feature struct {
Location *Point `protobuf:"bytes,2,opt,name=location" json:"location,omitempty"`
}
-func (m *Feature) Reset() { *m = Feature{} }
-func (m *Feature) String() string { return proto.CompactTextString(m) }
-func (*Feature) ProtoMessage() {}
+func (m *Feature) Reset() { *m = Feature{} }
+func (m *Feature) String() string { return proto.CompactTextString(m) }
+func (*Feature) ProtoMessage() {}
+func (*Feature) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
func (m *Feature) GetLocation() *Point {
if m != nil {
@@ -100,9 +107,10 @@ type RouteNote struct {
Message string `protobuf:"bytes,2,opt,name=message" json:"message,omitempty"`
}
-func (m *RouteNote) Reset() { *m = RouteNote{} }
-func (m *RouteNote) String() string { return proto.CompactTextString(m) }
-func (*RouteNote) ProtoMessage() {}
+func (m *RouteNote) Reset() { *m = RouteNote{} }
+func (m *RouteNote) String() string { return proto.CompactTextString(m) }
+func (*RouteNote) ProtoMessage() {}
+func (*RouteNote) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
func (m *RouteNote) GetLocation() *Point {
if m != nil {
@@ -127,9 +135,18 @@ type RouteSummary struct {
ElapsedTime int32 `protobuf:"varint,4,opt,name=elapsed_time" json:"elapsed_time,omitempty"`
}
-func (m *RouteSummary) Reset() { *m = RouteSummary{} }
-func (m *RouteSummary) String() string { return proto.CompactTextString(m) }
-func (*RouteSummary) ProtoMessage() {}
+func (m *RouteSummary) Reset() { *m = RouteSummary{} }
+func (m *RouteSummary) String() string { return proto.CompactTextString(m) }
+func (*RouteSummary) ProtoMessage() {}
+func (*RouteSummary) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
+
+func init() {
+ proto.RegisterType((*Point)(nil), "routeguide.Point")
+ proto.RegisterType((*Rectangle)(nil), "routeguide.Rectangle")
+ proto.RegisterType((*Feature)(nil), "routeguide.Feature")
+ proto.RegisterType((*RouteNote)(nil), "routeguide.RouteNote")
+ proto.RegisterType((*RouteSummary)(nil), "routeguide.RouteSummary")
+}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
@@ -142,8 +159,8 @@ type RouteGuideClient interface {
//
// Obtains the feature at a given position.
//
- // If no feature is found for the given point, a feature with an empty name
- // should be returned.
+ // A feature with an empty name is returned if there's no feature at the given
+ // position.
GetFeature(ctx context.Context, in *Point, opts ...grpc.CallOption) (*Feature, error)
// A server-to-client streaming RPC.
//
@@ -285,8 +302,8 @@ type RouteGuideServer interface {
//
// Obtains the feature at a given position.
//
- // If no feature is found for the given point, a feature with an empty name
- // should be returned.
+ // A feature with an empty name is returned if there's no feature at the given
+ // position.
GetFeature(context.Context, *Point) (*Feature, error)
// A server-to-client streaming RPC.
//
@@ -424,3 +441,33 @@ var _RouteGuide_serviceDesc = grpc.ServiceDesc{
},
},
}
+
+var fileDescriptor0 = []byte{
+ // 412 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x84, 0x53, 0xd1, 0x6a, 0xa3, 0x40,
+ 0x14, 0xcd, 0x98, 0x64, 0xb3, 0x5e, 0x5d, 0x96, 0xcc, 0xb2, 0x20, 0xd9, 0x85, 0xb6, 0xf6, 0x25,
+ 0x2f, 0x95, 0x90, 0x42, 0x1e, 0x5b, 0x9a, 0x40, 0xf3, 0x12, 0x4a, 0x6a, 0xf3, 0x1e, 0xa6, 0x3a,
+ 0x35, 0x03, 0xea, 0x88, 0x8e, 0xd0, 0x7e, 0x40, 0xbf, 0xa0, 0x7f, 0xd0, 0x2f, 0xed, 0x38, 0x6a,
+ 0x62, 0xda, 0x84, 0xbe, 0x39, 0xe7, 0x9e, 0x73, 0xef, 0xb9, 0xe7, 0x22, 0xf4, 0x53, 0x9e, 0x0b,
+ 0xba, 0x0e, 0x72, 0xe6, 0x53, 0x27, 0x49, 0xb9, 0xe0, 0x18, 0x14, 0xa4, 0x10, 0xfb, 0x06, 0xba,
+ 0x4b, 0xce, 0x62, 0x81, 0x07, 0xf0, 0x33, 0x24, 0x82, 0x89, 0xdc, 0xa7, 0x16, 0x3a, 0x45, 0xc3,
+ 0xae, 0xbb, 0x7d, 0xe3, 0xff, 0xa0, 0x87, 0x3c, 0x0e, 0xca, 0xa2, 0xa6, 0x8a, 0x3b, 0xc0, 0xbe,
+ 0x07, 0xdd, 0xa5, 0x9e, 0x20, 0x71, 0x10, 0x52, 0x7c, 0x06, 0x5a, 0xc8, 0x55, 0x03, 0x63, 0xdc,
+ 0x77, 0x76, 0x83, 0x1c, 0x35, 0xc5, 0x95, 0xc5, 0x82, 0xb2, 0x61, 0xaa, 0xcd, 0x61, 0xca, 0x86,
+ 0xd9, 0x0b, 0xe8, 0xdd, 0x52, 0x22, 0xf2, 0x94, 0x62, 0x0c, 0x9d, 0x98, 0x44, 0xa5, 0x27, 0xdd,
+ 0x55, 0xdf, 0xf8, 0x42, 0x7a, 0xe5, 0x9e, 0x74, 0xc7, 0xe3, 0xe3, 0x7d, 0xb6, 0x14, 0x7b, 0x25,
+ 0x0d, 0x16, 0xd5, 0x3b, 0x2e, 0xf6, 0xb5, 0xe8, 0x5b, 0x2d, 0xb6, 0xa0, 0x17, 0xd1, 0x2c, 0x23,
+ 0x41, 0xb9, 0xb8, 0xee, 0xd6, 0x4f, 0xfb, 0x0d, 0x81, 0xa9, 0xda, 0x3e, 0xe4, 0x51, 0x44, 0xd2,
+ 0x17, 0x7c, 0x02, 0x46, 0x52, 0xa8, 0xd7, 0x1e, 0xcf, 0x63, 0x51, 0x85, 0x08, 0x0a, 0x9a, 0x15,
+ 0x08, 0x3e, 0x87, 0x5f, 0x4f, 0xe5, 0x56, 0x15, 0xa5, 0x8c, 0xd2, 0xac, 0xc0, 0x92, 0x24, 0xef,
+ 0xe0, 0xb3, 0x4c, 0xa6, 0xe9, 0x51, 0xab, 0x5d, 0xde, 0xa1, 0x7e, 0xcb, 0xe4, 0x4c, 0x1a, 0x92,
+ 0x24, 0xa3, 0xfe, 0x5a, 0x30, 0x99, 0x49, 0x47, 0xd5, 0x8d, 0x0a, 0x5b, 0x49, 0x68, 0xfc, 0xaa,
+ 0x01, 0x28, 0x57, 0xf3, 0x62, 0x1d, 0x3c, 0x01, 0x98, 0x53, 0x51, 0x67, 0xf9, 0x75, 0xd3, 0xc1,
+ 0x9f, 0x26, 0x54, 0xf1, 0xec, 0x16, 0xbe, 0x02, 0x73, 0x21, 0xa7, 0x56, 0x40, 0x86, 0xff, 0x36,
+ 0x69, 0xdb, 0x6b, 0x1f, 0x51, 0x8f, 0x90, 0xd4, 0x1b, 0x92, 0xc5, 0x53, 0x5f, 0x79, 0x39, 0x34,
+ 0xd8, 0xda, 0xeb, 0xd8, 0xc8, 0xd1, 0x6e, 0x0d, 0x11, 0xbe, 0xae, 0x4e, 0x36, 0xdb, 0x10, 0xf1,
+ 0x69, 0x78, 0x7d, 0xc9, 0xc1, 0x61, 0xb8, 0x90, 0x8f, 0xd0, 0x74, 0x02, 0xff, 0x18, 0x77, 0x82,
+ 0x34, 0xf1, 0x1c, 0xfa, 0x4c, 0xa2, 0x24, 0xa4, 0x59, 0x83, 0x3e, 0xfd, 0xbd, 0xcb, 0x68, 0x59,
+ 0xfc, 0x13, 0x4b, 0xf4, 0xae, 0xb5, 0xdd, 0xd5, 0xfc, 0xf1, 0x87, 0xfa, 0x45, 0x2e, 0x3f, 0x02,
+ 0x00, 0x00, 0xff, 0xff, 0xf3, 0xe2, 0x76, 0x5e, 0x37, 0x03, 0x00, 0x00,
+}
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/routeguide/route_guide.proto b/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/routeguide/route_guide.proto
index bee7ac51ab3..12c4495ffab 100644
--- a/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/routeguide/route_guide.proto
+++ b/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/routeguide/route_guide.proto
@@ -29,6 +29,11 @@
syntax = "proto3";
+option java_multiple_files = true;
+option java_package = "io.grpc.examples.routeguide";
+option java_outer_classname = "RouteGuideProto";
+option objc_class_prefix = "RTG";
+
package routeguide;
// Interface exported by the server.
@@ -37,8 +42,8 @@ service RouteGuide {
//
// Obtains the feature at a given position.
//
- // If no feature is found for the given point, a feature with an empty name
- // should be returned.
+ // A feature with an empty name is returned if there's no feature at the given
+ // position.
rpc GetFeature(Point) returns (Feature) {}
// A server-to-client streaming RPC.
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/testdata/ca.pem b/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/testdata/ca.pem
deleted file mode 100644
index 6c8511a73c6..00000000000
--- a/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/testdata/ca.pem
+++ /dev/null
@@ -1,15 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV
-BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
-aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla
-Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0
-YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT
-BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7
-+L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu
-g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd
-Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV
-HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau
-sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m
-oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG
-Dfcog5wrJytaQ6UA0wE=
------END CERTIFICATE-----
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/testdata/route_guide_db.json b/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/testdata/route_guide_db.json
deleted file mode 100644
index 9d6a980ab7d..00000000000
--- a/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/testdata/route_guide_db.json
+++ /dev/null
@@ -1,601 +0,0 @@
-[{
- "location": {
- "latitude": 407838351,
- "longitude": -746143763
- },
- "name": "Patriots Path, Mendham, NJ 07945, USA"
-}, {
- "location": {
- "latitude": 408122808,
- "longitude": -743999179
- },
- "name": "101 New Jersey 10, Whippany, NJ 07981, USA"
-}, {
- "location": {
- "latitude": 413628156,
- "longitude": -749015468
- },
- "name": "U.S. 6, Shohola, PA 18458, USA"
-}, {
- "location": {
- "latitude": 419999544,
- "longitude": -740371136
- },
- "name": "5 Conners Road, Kingston, NY 12401, USA"
-}, {
- "location": {
- "latitude": 414008389,
- "longitude": -743951297
- },
- "name": "Mid Hudson Psychiatric Center, New Hampton, NY 10958, USA"
-}, {
- "location": {
- "latitude": 419611318,
- "longitude": -746524769
- },
- "name": "287 Flugertown Road, Livingston Manor, NY 12758, USA"
-}, {
- "location": {
- "latitude": 406109563,
- "longitude": -742186778
- },
- "name": "4001 Tremley Point Road, Linden, NJ 07036, USA"
-}, {
- "location": {
- "latitude": 416802456,
- "longitude": -742370183
- },
- "name": "352 South Mountain Road, Wallkill, NY 12589, USA"
-}, {
- "location": {
- "latitude": 412950425,
- "longitude": -741077389
- },
- "name": "Bailey Turn Road, Harriman, NY 10926, USA"
-}, {
- "location": {
- "latitude": 412144655,
- "longitude": -743949739
- },
- "name": "193-199 Wawayanda Road, Hewitt, NJ 07421, USA"
-}, {
- "location": {
- "latitude": 415736605,
- "longitude": -742847522
- },
- "name": "406-496 Ward Avenue, Pine Bush, NY 12566, USA"
-}, {
- "location": {
- "latitude": 413843930,
- "longitude": -740501726
- },
- "name": "162 Merrill Road, Highland Mills, NY 10930, USA"
-}, {
- "location": {
- "latitude": 410873075,
- "longitude": -744459023
- },
- "name": "Clinton Road, West Milford, NJ 07480, USA"
-}, {
- "location": {
- "latitude": 412346009,
- "longitude": -744026814
- },
- "name": "16 Old Brook Lane, Warwick, NY 10990, USA"
-}, {
- "location": {
- "latitude": 402948455,
- "longitude": -747903913
- },
- "name": "3 Drake Lane, Pennington, NJ 08534, USA"
-}, {
- "location": {
- "latitude": 406337092,
- "longitude": -740122226
- },
- "name": "6324 8th Avenue, Brooklyn, NY 11220, USA"
-}, {
- "location": {
- "latitude": 406421967,
- "longitude": -747727624
- },
- "name": "1 Merck Access Road, Whitehouse Station, NJ 08889, USA"
-}, {
- "location": {
- "latitude": 416318082,
- "longitude": -749677716
- },
- "name": "78-98 Schalck Road, Narrowsburg, NY 12764, USA"
-}, {
- "location": {
- "latitude": 415301720,
- "longitude": -748416257
- },
- "name": "282 Lakeview Drive Road, Highland Lake, NY 12743, USA"
-}, {
- "location": {
- "latitude": 402647019,
- "longitude": -747071791
- },
- "name": "330 Evelyn Avenue, Hamilton Township, NJ 08619, USA"
-}, {
- "location": {
- "latitude": 412567807,
- "longitude": -741058078
- },
- "name": "New York State Reference Route 987E, Southfields, NY 10975, USA"
-}, {
- "location": {
- "latitude": 416855156,
- "longitude": -744420597
- },
- "name": "103-271 Tempaloni Road, Ellenville, NY 12428, USA"
-}, {
- "location": {
- "latitude": 404663628,
- "longitude": -744820157
- },
- "name": "1300 Airport Road, North Brunswick Township, NJ 08902, USA"
-}, {
- "location": {
- "latitude": 407113723,
- "longitude": -749746483
- },
- "name": ""
-}, {
- "location": {
- "latitude": 402133926,
- "longitude": -743613249
- },
- "name": ""
-}, {
- "location": {
- "latitude": 400273442,
- "longitude": -741220915
- },
- "name": ""
-}, {
- "location": {
- "latitude": 411236786,
- "longitude": -744070769
- },
- "name": ""
-}, {
- "location": {
- "latitude": 411633782,
- "longitude": -746784970
- },
- "name": "211-225 Plains Road, Augusta, NJ 07822, USA"
-}, {
- "location": {
- "latitude": 415830701,
- "longitude": -742952812
- },
- "name": ""
-}, {
- "location": {
- "latitude": 413447164,
- "longitude": -748712898
- },
- "name": "165 Pedersen Ridge Road, Milford, PA 18337, USA"
-}, {
- "location": {
- "latitude": 405047245,
- "longitude": -749800722
- },
- "name": "100-122 Locktown Road, Frenchtown, NJ 08825, USA"
-}, {
- "location": {
- "latitude": 418858923,
- "longitude": -746156790
- },
- "name": ""
-}, {
- "location": {
- "latitude": 417951888,
- "longitude": -748484944
- },
- "name": "650-652 Willi Hill Road, Swan Lake, NY 12783, USA"
-}, {
- "location": {
- "latitude": 407033786,
- "longitude": -743977337
- },
- "name": "26 East 3rd Street, New Providence, NJ 07974, USA"
-}, {
- "location": {
- "latitude": 417548014,
- "longitude": -740075041
- },
- "name": ""
-}, {
- "location": {
- "latitude": 410395868,
- "longitude": -744972325
- },
- "name": ""
-}, {
- "location": {
- "latitude": 404615353,
- "longitude": -745129803
- },
- "name": ""
-}, {
- "location": {
- "latitude": 406589790,
- "longitude": -743560121
- },
- "name": "611 Lawrence Avenue, Westfield, NJ 07090, USA"
-}, {
- "location": {
- "latitude": 414653148,
- "longitude": -740477477
- },
- "name": "18 Lannis Avenue, New Windsor, NY 12553, USA"
-}, {
- "location": {
- "latitude": 405957808,
- "longitude": -743255336
- },
- "name": "82-104 Amherst Avenue, Colonia, NJ 07067, USA"
-}, {
- "location": {
- "latitude": 411733589,
- "longitude": -741648093
- },
- "name": "170 Seven Lakes Drive, Sloatsburg, NY 10974, USA"
-}, {
- "location": {
- "latitude": 412676291,
- "longitude": -742606606
- },
- "name": "1270 Lakes Road, Monroe, NY 10950, USA"
-}, {
- "location": {
- "latitude": 409224445,
- "longitude": -748286738
- },
- "name": "509-535 Alphano Road, Great Meadows, NJ 07838, USA"
-}, {
- "location": {
- "latitude": 406523420,
- "longitude": -742135517
- },
- "name": "652 Garden Street, Elizabeth, NJ 07202, USA"
-}, {
- "location": {
- "latitude": 401827388,
- "longitude": -740294537
- },
- "name": "349 Sea Spray Court, Neptune City, NJ 07753, USA"
-}, {
- "location": {
- "latitude": 410564152,
- "longitude": -743685054
- },
- "name": "13-17 Stanley Street, West Milford, NJ 07480, USA"
-}, {
- "location": {
- "latitude": 408472324,
- "longitude": -740726046
- },
- "name": "47 Industrial Avenue, Teterboro, NJ 07608, USA"
-}, {
- "location": {
- "latitude": 412452168,
- "longitude": -740214052
- },
- "name": "5 White Oak Lane, Stony Point, NY 10980, USA"
-}, {
- "location": {
- "latitude": 409146138,
- "longitude": -746188906
- },
- "name": "Berkshire Valley Management Area Trail, Jefferson, NJ, USA"
-}, {
- "location": {
- "latitude": 404701380,
- "longitude": -744781745
- },
- "name": "1007 Jersey Avenue, New Brunswick, NJ 08901, USA"
-}, {
- "location": {
- "latitude": 409642566,
- "longitude": -746017679
- },
- "name": "6 East Emerald Isle Drive, Lake Hopatcong, NJ 07849, USA"
-}, {
- "location": {
- "latitude": 408031728,
- "longitude": -748645385
- },
- "name": "1358-1474 New Jersey 57, Port Murray, NJ 07865, USA"
-}, {
- "location": {
- "latitude": 413700272,
- "longitude": -742135189
- },
- "name": "367 Prospect Road, Chester, NY 10918, USA"
-}, {
- "location": {
- "latitude": 404310607,
- "longitude": -740282632
- },
- "name": "10 Simon Lake Drive, Atlantic Highlands, NJ 07716, USA"
-}, {
- "location": {
- "latitude": 409319800,
- "longitude": -746201391
- },
- "name": "11 Ward Street, Mount Arlington, NJ 07856, USA"
-}, {
- "location": {
- "latitude": 406685311,
- "longitude": -742108603
- },
- "name": "300-398 Jefferson Avenue, Elizabeth, NJ 07201, USA"
-}, {
- "location": {
- "latitude": 419018117,
- "longitude": -749142781
- },
- "name": "43 Dreher Road, Roscoe, NY 12776, USA"
-}, {
- "location": {
- "latitude": 412856162,
- "longitude": -745148837
- },
- "name": "Swan Street, Pine Island, NY 10969, USA"
-}, {
- "location": {
- "latitude": 416560744,
- "longitude": -746721964
- },
- "name": "66 Pleasantview Avenue, Monticello, NY 12701, USA"
-}, {
- "location": {
- "latitude": 405314270,
- "longitude": -749836354
- },
- "name": ""
-}, {
- "location": {
- "latitude": 414219548,
- "longitude": -743327440
- },
- "name": ""
-}, {
- "location": {
- "latitude": 415534177,
- "longitude": -742900616
- },
- "name": "565 Winding Hills Road, Montgomery, NY 12549, USA"
-}, {
- "location": {
- "latitude": 406898530,
- "longitude": -749127080
- },
- "name": "231 Rocky Run Road, Glen Gardner, NJ 08826, USA"
-}, {
- "location": {
- "latitude": 407586880,
- "longitude": -741670168
- },
- "name": "100 Mount Pleasant Avenue, Newark, NJ 07104, USA"
-}, {
- "location": {
- "latitude": 400106455,
- "longitude": -742870190
- },
- "name": "517-521 Huntington Drive, Manchester Township, NJ 08759, USA"
-}, {
- "location": {
- "latitude": 400066188,
- "longitude": -746793294
- },
- "name": ""
-}, {
- "location": {
- "latitude": 418803880,
- "longitude": -744102673
- },
- "name": "40 Mountain Road, Napanoch, NY 12458, USA"
-}, {
- "location": {
- "latitude": 414204288,
- "longitude": -747895140
- },
- "name": ""
-}, {
- "location": {
- "latitude": 414777405,
- "longitude": -740615601
- },
- "name": ""
-}, {
- "location": {
- "latitude": 415464475,
- "longitude": -747175374
- },
- "name": "48 North Road, Forestburgh, NY 12777, USA"
-}, {
- "location": {
- "latitude": 404062378,
- "longitude": -746376177
- },
- "name": ""
-}, {
- "location": {
- "latitude": 405688272,
- "longitude": -749285130
- },
- "name": ""
-}, {
- "location": {
- "latitude": 400342070,
- "longitude": -748788996
- },
- "name": ""
-}, {
- "location": {
- "latitude": 401809022,
- "longitude": -744157964
- },
- "name": ""
-}, {
- "location": {
- "latitude": 404226644,
- "longitude": -740517141
- },
- "name": "9 Thompson Avenue, Leonardo, NJ 07737, USA"
-}, {
- "location": {
- "latitude": 410322033,
- "longitude": -747871659
- },
- "name": ""
-}, {
- "location": {
- "latitude": 407100674,
- "longitude": -747742727
- },
- "name": ""
-}, {
- "location": {
- "latitude": 418811433,
- "longitude": -741718005
- },
- "name": "213 Bush Road, Stone Ridge, NY 12484, USA"
-}, {
- "location": {
- "latitude": 415034302,
- "longitude": -743850945
- },
- "name": ""
-}, {
- "location": {
- "latitude": 411349992,
- "longitude": -743694161
- },
- "name": ""
-}, {
- "location": {
- "latitude": 404839914,
- "longitude": -744759616
- },
- "name": "1-17 Bergen Court, New Brunswick, NJ 08901, USA"
-}, {
- "location": {
- "latitude": 414638017,
- "longitude": -745957854
- },
- "name": "35 Oakland Valley Road, Cuddebackville, NY 12729, USA"
-}, {
- "location": {
- "latitude": 412127800,
- "longitude": -740173578
- },
- "name": ""
-}, {
- "location": {
- "latitude": 401263460,
- "longitude": -747964303
- },
- "name": ""
-}, {
- "location": {
- "latitude": 412843391,
- "longitude": -749086026
- },
- "name": ""
-}, {
- "location": {
- "latitude": 418512773,
- "longitude": -743067823
- },
- "name": ""
-}, {
- "location": {
- "latitude": 404318328,
- "longitude": -740835638
- },
- "name": "42-102 Main Street, Belford, NJ 07718, USA"
-}, {
- "location": {
- "latitude": 419020746,
- "longitude": -741172328
- },
- "name": ""
-}, {
- "location": {
- "latitude": 404080723,
- "longitude": -746119569
- },
- "name": ""
-}, {
- "location": {
- "latitude": 401012643,
- "longitude": -744035134
- },
- "name": ""
-}, {
- "location": {
- "latitude": 404306372,
- "longitude": -741079661
- },
- "name": ""
-}, {
- "location": {
- "latitude": 403966326,
- "longitude": -748519297
- },
- "name": ""
-}, {
- "location": {
- "latitude": 405002031,
- "longitude": -748407866
- },
- "name": ""
-}, {
- "location": {
- "latitude": 409532885,
- "longitude": -742200683
- },
- "name": ""
-}, {
- "location": {
- "latitude": 416851321,
- "longitude": -742674555
- },
- "name": ""
-}, {
- "location": {
- "latitude": 406411633,
- "longitude": -741722051
- },
- "name": "3387 Richmond Terrace, Staten Island, NY 10303, USA"
-}, {
- "location": {
- "latitude": 413069058,
- "longitude": -744597778
- },
- "name": "261 Van Sickle Road, Goshen, NY 10924, USA"
-}, {
- "location": {
- "latitude": 418465462,
- "longitude": -746859398
- },
- "name": ""
-}, {
- "location": {
- "latitude": 411733222,
- "longitude": -744228360
- },
- "name": ""
-}, {
- "location": {
- "latitude": 410248224,
- "longitude": -747127767
- },
- "name": "3 Hasta Way, Newton, NJ 07860, USA"
-}]
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/testdata/server1.key b/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/testdata/server1.key
deleted file mode 100644
index 143a5b87658..00000000000
--- a/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/testdata/server1.key
+++ /dev/null
@@ -1,16 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOHDFScoLCVJpYDD
-M4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1BgzkWF+slf
-3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd9N8YwbBY
-AckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAECgYAn7qGnM2vbjJNBm0VZCkOkTIWm
-V10okw7EPJrdL2mkre9NasghNXbE1y5zDshx5Nt3KsazKOxTT8d0Jwh/3KbaN+YY
-tTCbKGW0pXDRBhwUHRcuRzScjli8Rih5UOCiZkhefUTcRb6xIhZJuQy71tjaSy0p
-dHZRmYyBYO2YEQ8xoQJBAPrJPhMBkzmEYFtyIEqAxQ/o/A6E+E4w8i+KM7nQCK7q
-K4JXzyXVAjLfyBZWHGM2uro/fjqPggGD6QH1qXCkI4MCQQDmdKeb2TrKRh5BY1LR
-81aJGKcJ2XbcDu6wMZK4oqWbTX2KiYn9GB0woM6nSr/Y6iy1u145YzYxEV/iMwff
-DJULAkB8B2MnyzOg0pNFJqBJuH29bKCcHa8gHJzqXhNO5lAlEbMK95p/P2Wi+4Hd
-aiEIAF1BF326QJcvYKmwSmrORp85AkAlSNxRJ50OWrfMZnBgzVjDx3xG6KsFQVk2
-ol6VhqL6dFgKUORFUWBvnKSyhjJxurlPEahV6oo6+A+mPhFY8eUvAkAZQyTdupP3
-XEFQKctGz+9+gKkemDp7LBBMEMBXrGTLPhpEfcjv/7KPdnFHYmhYeBTBnuVmTVWe
-F98XJ7tIFfJq
------END PRIVATE KEY-----
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/testdata/server1.pem b/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/testdata/server1.pem
deleted file mode 100644
index f3d43fcc5be..00000000000
--- a/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/testdata/server1.pem
+++ /dev/null
@@ -1,16 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICnDCCAgWgAwIBAgIBBzANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJBVTET
-MBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ
-dHkgTHRkMQ8wDQYDVQQDEwZ0ZXN0Y2EwHhcNMTUxMTA0MDIyMDI0WhcNMjUxMTAx
-MDIyMDI0WjBlMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV
-BAcTB0NoaWNhZ28xFTATBgNVBAoTDEV4YW1wbGUsIENvLjEaMBgGA1UEAxQRKi50
-ZXN0Lmdvb2dsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOHDFSco
-LCVJpYDDM4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1Bg
-zkWF+slf3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd
-9N8YwbBYAckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAGjazBpMAkGA1UdEwQCMAAw
-CwYDVR0PBAQDAgXgME8GA1UdEQRIMEaCECoudGVzdC5nb29nbGUuZnKCGHdhdGVy
-em9vaS50ZXN0Lmdvb2dsZS5iZYISKi50ZXN0LnlvdXR1YmUuY29thwTAqAEDMA0G
-CSqGSIb3DQEBCwUAA4GBAJFXVifQNub1LUP4JlnX5lXNlo8FxZ2a12AFQs+bzoJ6
-hM044EDjqyxUqSbVePK0ni3w1fHQB5rY9yYC5f8G7aqqTY1QOhoUk8ZTSTRpnkTh
-y4jjdvTZeLDVBlueZUTDRmy2feY5aZIU18vFDK08dTG0A87pppuv1LNIR3loveU8
------END CERTIFICATE-----
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/grpclog/logger.go b/Godeps/_workspace/src/google.golang.org/grpc/grpclog/logger.go
index cc6e27c0649..2cc09be4894 100644
--- a/Godeps/_workspace/src/google.golang.org/grpc/grpclog/logger.go
+++ b/Godeps/_workspace/src/google.golang.org/grpc/grpclog/logger.go
@@ -42,6 +42,8 @@ import (
)
// Use golang's standard logger by default.
+// Access is not mutex-protected: do not modify except in init()
+// functions.
var logger Logger = log.New(os.Stderr, "", log.LstdFlags)
// Logger mimics golang's standard Logger as an interface.
@@ -54,7 +56,8 @@ type Logger interface {
Println(args ...interface{})
}
-// SetLogger sets the logger that is used in grpc.
+// SetLogger sets the logger that is used in grpc. Call only from
+// init() functions.
func SetLogger(l Logger) {
logger = l
}
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/health/grpc_health_v1alpha/health.pb.go b/Godeps/_workspace/src/google.golang.org/grpc/health/grpc_health_v1/health.pb.go
similarity index 50%
rename from Godeps/_workspace/src/google.golang.org/grpc/health/grpc_health_v1alpha/health.pb.go
rename to Godeps/_workspace/src/google.golang.org/grpc/health/grpc_health_v1/health.pb.go
index 1083bc63bc5..e0b040ebeb1 100644
--- a/Godeps/_workspace/src/google.golang.org/grpc/health/grpc_health_v1alpha/health.pb.go
+++ b/Godeps/_workspace/src/google.golang.org/grpc/health/grpc_health_v1/health.pb.go
@@ -3,7 +3,7 @@
// DO NOT EDIT!
/*
-Package grpc_health_v1alpha is a generated protocol buffer package.
+Package grpc_health_v1 is a generated protocol buffer package.
It is generated from these files:
health.proto
@@ -12,7 +12,7 @@ It has these top-level messages:
HealthCheckRequest
HealthCheckResponse
*/
-package grpc_health_v1alpha
+package grpc_health_v1
import proto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
import fmt "fmt"
@@ -28,6 +28,10 @@ var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+const _ = proto.ProtoPackageIsVersion1
+
type HealthCheckResponse_ServingStatus int32
const (
@@ -50,25 +54,32 @@ var HealthCheckResponse_ServingStatus_value = map[string]int32{
func (x HealthCheckResponse_ServingStatus) String() string {
return proto.EnumName(HealthCheckResponse_ServingStatus_name, int32(x))
}
+func (HealthCheckResponse_ServingStatus) EnumDescriptor() ([]byte, []int) {
+ return fileDescriptor0, []int{1, 0}
+}
type HealthCheckRequest struct {
Service string `protobuf:"bytes,1,opt,name=service" json:"service,omitempty"`
}
-func (m *HealthCheckRequest) Reset() { *m = HealthCheckRequest{} }
-func (m *HealthCheckRequest) String() string { return proto.CompactTextString(m) }
-func (*HealthCheckRequest) ProtoMessage() {}
+func (m *HealthCheckRequest) Reset() { *m = HealthCheckRequest{} }
+func (m *HealthCheckRequest) String() string { return proto.CompactTextString(m) }
+func (*HealthCheckRequest) ProtoMessage() {}
+func (*HealthCheckRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
type HealthCheckResponse struct {
- Status HealthCheckResponse_ServingStatus `protobuf:"varint,1,opt,name=status,enum=grpc.health.v1alpha.HealthCheckResponse_ServingStatus" json:"status,omitempty"`
+ Status HealthCheckResponse_ServingStatus `protobuf:"varint,1,opt,name=status,enum=grpc.health.v1.HealthCheckResponse_ServingStatus" json:"status,omitempty"`
}
-func (m *HealthCheckResponse) Reset() { *m = HealthCheckResponse{} }
-func (m *HealthCheckResponse) String() string { return proto.CompactTextString(m) }
-func (*HealthCheckResponse) ProtoMessage() {}
+func (m *HealthCheckResponse) Reset() { *m = HealthCheckResponse{} }
+func (m *HealthCheckResponse) String() string { return proto.CompactTextString(m) }
+func (*HealthCheckResponse) ProtoMessage() {}
+func (*HealthCheckResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func init() {
- proto.RegisterEnum("grpc.health.v1alpha.HealthCheckResponse_ServingStatus", HealthCheckResponse_ServingStatus_name, HealthCheckResponse_ServingStatus_value)
+ proto.RegisterType((*HealthCheckRequest)(nil), "grpc.health.v1.HealthCheckRequest")
+ proto.RegisterType((*HealthCheckResponse)(nil), "grpc.health.v1.HealthCheckResponse")
+ proto.RegisterEnum("grpc.health.v1.HealthCheckResponse_ServingStatus", HealthCheckResponse_ServingStatus_name, HealthCheckResponse_ServingStatus_value)
}
// Reference imports to suppress errors if they are not otherwise used.
@@ -91,7 +102,7 @@ func NewHealthClient(cc *grpc.ClientConn) HealthClient {
func (c *healthClient) Check(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (*HealthCheckResponse, error) {
out := new(HealthCheckResponse)
- err := grpc.Invoke(ctx, "/grpc.health.v1alpha.Health/Check", in, out, c.cc, opts...)
+ err := grpc.Invoke(ctx, "/grpc.health.v1.Health/Check", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
@@ -121,7 +132,7 @@ func _Health_Check_Handler(srv interface{}, ctx context.Context, dec func(interf
}
var _Health_serviceDesc = grpc.ServiceDesc{
- ServiceName: "grpc.health.v1alpha.Health",
+ ServiceName: "grpc.health.v1.Health",
HandlerType: (*HealthServer)(nil),
Methods: []grpc.MethodDesc{
{
@@ -131,3 +142,21 @@ var _Health_serviceDesc = grpc.ServiceDesc{
},
Streams: []grpc.StreamDesc{},
}
+
+var fileDescriptor0 = []byte{
+ // 209 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0xc9, 0x48, 0x4d, 0xcc,
+ 0x29, 0xc9, 0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x4e, 0x2f, 0x2a, 0x48, 0xd6, 0x83,
+ 0x0a, 0x95, 0x19, 0x26, 0xe6, 0x14, 0x64, 0x24, 0x2a, 0xe9, 0x71, 0x09, 0x79, 0x80, 0x45, 0x9c,
+ 0x33, 0x52, 0x93, 0xb3, 0x83, 0x52, 0x0b, 0x4b, 0x53, 0x8b, 0x4b, 0x84, 0x24, 0xb8, 0xd8, 0x8b,
+ 0x53, 0x8b, 0xca, 0x32, 0x93, 0x53, 0x25, 0x18, 0x15, 0x18, 0x35, 0x38, 0x83, 0x60, 0x5c, 0xa5,
+ 0x85, 0x8c, 0x5c, 0xc2, 0x28, 0x1a, 0x8a, 0x0b, 0xf2, 0xf3, 0x8a, 0x53, 0x85, 0xfc, 0xb8, 0xd8,
+ 0x8a, 0x4b, 0x12, 0x4b, 0x4a, 0x8b, 0xc1, 0x1a, 0xf8, 0x8c, 0xcc, 0xf4, 0xb0, 0xd8, 0xa6, 0x87,
+ 0x45, 0xa7, 0x5e, 0x30, 0xc8, 0xe4, 0xbc, 0xf4, 0x60, 0xb0, 0xee, 0x20, 0xa8, 0x29, 0x4a, 0x56,
+ 0x5c, 0xbc, 0x28, 0x12, 0x42, 0xdc, 0x5c, 0xec, 0xa1, 0x7e, 0xde, 0x7e, 0xfe, 0xe1, 0x7e, 0x02,
+ 0x0c, 0x20, 0x4e, 0xb0, 0x6b, 0x50, 0x98, 0xa7, 0x9f, 0xbb, 0x00, 0xa3, 0x10, 0x3f, 0x17, 0xb7,
+ 0x9f, 0x7f, 0x48, 0x3c, 0x4c, 0x80, 0xc9, 0x28, 0x85, 0x8b, 0x0d, 0x62, 0x91, 0x50, 0x14, 0x17,
+ 0x2b, 0xd8, 0x32, 0x21, 0x75, 0xc2, 0xce, 0x01, 0xfb, 0x5c, 0x4a, 0x83, 0x58, 0x77, 0x27, 0xb1,
+ 0x81, 0x43, 0xd5, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0xe1, 0x3f, 0xd0, 0xe1, 0x65, 0x01, 0x00,
+ 0x00,
+}
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/health/grpc_health_v1alpha/health.proto b/Godeps/_workspace/src/google.golang.org/grpc/health/grpc_health_v1/health.proto
similarity index 90%
rename from Godeps/_workspace/src/google.golang.org/grpc/health/grpc_health_v1alpha/health.proto
rename to Godeps/_workspace/src/google.golang.org/grpc/health/grpc_health_v1/health.proto
index 91c7f06e9b2..e2dc0889258 100644
--- a/Godeps/_workspace/src/google.golang.org/grpc/health/grpc_health_v1alpha/health.proto
+++ b/Godeps/_workspace/src/google.golang.org/grpc/health/grpc_health_v1/health.proto
@@ -1,6 +1,6 @@
syntax = "proto3";
-package grpc.health.v1alpha;
+package grpc.health.v1;
message HealthCheckRequest {
string service = 1;
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/health/health.go b/Godeps/_workspace/src/google.golang.org/grpc/health/health.go
index ac42d49f6d5..95c6cb157b8 100644
--- a/Godeps/_workspace/src/google.golang.org/grpc/health/health.go
+++ b/Godeps/_workspace/src/google.golang.org/grpc/health/health.go
@@ -8,7 +8,7 @@ import (
"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
"github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc"
"github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codes"
- healthpb "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/health/grpc_health_v1alpha"
+ healthpb "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/health/grpc_health_v1"
)
type HealthServer struct {
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/interop/client/testdata/ca.pem b/Godeps/_workspace/src/google.golang.org/grpc/interop/client/testdata/ca.pem
deleted file mode 100644
index 6c8511a73c6..00000000000
--- a/Godeps/_workspace/src/google.golang.org/grpc/interop/client/testdata/ca.pem
+++ /dev/null
@@ -1,15 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV
-BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
-aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla
-Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0
-YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT
-BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7
-+L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu
-g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd
-Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV
-HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau
-sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m
-oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG
-Dfcog5wrJytaQ6UA0wE=
------END CERTIFICATE-----
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/interop/client/testdata/server1.key b/Godeps/_workspace/src/google.golang.org/grpc/interop/client/testdata/server1.key
deleted file mode 100644
index 143a5b87658..00000000000
--- a/Godeps/_workspace/src/google.golang.org/grpc/interop/client/testdata/server1.key
+++ /dev/null
@@ -1,16 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOHDFScoLCVJpYDD
-M4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1BgzkWF+slf
-3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd9N8YwbBY
-AckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAECgYAn7qGnM2vbjJNBm0VZCkOkTIWm
-V10okw7EPJrdL2mkre9NasghNXbE1y5zDshx5Nt3KsazKOxTT8d0Jwh/3KbaN+YY
-tTCbKGW0pXDRBhwUHRcuRzScjli8Rih5UOCiZkhefUTcRb6xIhZJuQy71tjaSy0p
-dHZRmYyBYO2YEQ8xoQJBAPrJPhMBkzmEYFtyIEqAxQ/o/A6E+E4w8i+KM7nQCK7q
-K4JXzyXVAjLfyBZWHGM2uro/fjqPggGD6QH1qXCkI4MCQQDmdKeb2TrKRh5BY1LR
-81aJGKcJ2XbcDu6wMZK4oqWbTX2KiYn9GB0woM6nSr/Y6iy1u145YzYxEV/iMwff
-DJULAkB8B2MnyzOg0pNFJqBJuH29bKCcHa8gHJzqXhNO5lAlEbMK95p/P2Wi+4Hd
-aiEIAF1BF326QJcvYKmwSmrORp85AkAlSNxRJ50OWrfMZnBgzVjDx3xG6KsFQVk2
-ol6VhqL6dFgKUORFUWBvnKSyhjJxurlPEahV6oo6+A+mPhFY8eUvAkAZQyTdupP3
-XEFQKctGz+9+gKkemDp7LBBMEMBXrGTLPhpEfcjv/7KPdnFHYmhYeBTBnuVmTVWe
-F98XJ7tIFfJq
------END PRIVATE KEY-----
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/interop/client/testdata/server1.pem b/Godeps/_workspace/src/google.golang.org/grpc/interop/client/testdata/server1.pem
deleted file mode 100644
index f3d43fcc5be..00000000000
--- a/Godeps/_workspace/src/google.golang.org/grpc/interop/client/testdata/server1.pem
+++ /dev/null
@@ -1,16 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICnDCCAgWgAwIBAgIBBzANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJBVTET
-MBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ
-dHkgTHRkMQ8wDQYDVQQDEwZ0ZXN0Y2EwHhcNMTUxMTA0MDIyMDI0WhcNMjUxMTAx
-MDIyMDI0WjBlMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV
-BAcTB0NoaWNhZ28xFTATBgNVBAoTDEV4YW1wbGUsIENvLjEaMBgGA1UEAxQRKi50
-ZXN0Lmdvb2dsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOHDFSco
-LCVJpYDDM4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1Bg
-zkWF+slf3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd
-9N8YwbBYAckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAGjazBpMAkGA1UdEwQCMAAw
-CwYDVR0PBAQDAgXgME8GA1UdEQRIMEaCECoudGVzdC5nb29nbGUuZnKCGHdhdGVy
-em9vaS50ZXN0Lmdvb2dsZS5iZYISKi50ZXN0LnlvdXR1YmUuY29thwTAqAEDMA0G
-CSqGSIb3DQEBCwUAA4GBAJFXVifQNub1LUP4JlnX5lXNlo8FxZ2a12AFQs+bzoJ6
-hM044EDjqyxUqSbVePK0ni3w1fHQB5rY9yYC5f8G7aqqTY1QOhoUk8ZTSTRpnkTh
-y4jjdvTZeLDVBlueZUTDRmy2feY5aZIU18vFDK08dTG0A87pppuv1LNIR3loveU8
------END CERTIFICATE-----
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/interop/grpc_testing/test.pb.go b/Godeps/_workspace/src/google.golang.org/grpc/interop/grpc_testing/test.pb.go
index 158379eb8cc..af982088ddf 100644
--- a/Godeps/_workspace/src/google.golang.org/grpc/interop/grpc_testing/test.pb.go
+++ b/Godeps/_workspace/src/google.golang.org/grpc/interop/grpc_testing/test.pb.go
@@ -22,6 +22,7 @@ It has these top-level messages:
package grpc_testing
import proto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+import fmt "fmt"
import math "math"
import (
@@ -29,14 +30,15 @@ import (
grpc "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc"
)
-// Reference imports to suppress errors if they are not otherwise used.
-var _ context.Context
-var _ grpc.ClientConn
-
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
+var _ = fmt.Errorf
var _ = math.Inf
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+const _ = proto.ProtoPackageIsVersion1
+
// The type of payload that should be returned.
type PayloadType int32
@@ -76,14 +78,16 @@ func (x *PayloadType) UnmarshalJSON(data []byte) error {
*x = PayloadType(value)
return nil
}
+func (PayloadType) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
type Empty struct {
XXX_unrecognized []byte `json:"-"`
}
-func (m *Empty) Reset() { *m = Empty{} }
-func (m *Empty) String() string { return proto.CompactTextString(m) }
-func (*Empty) ProtoMessage() {}
+func (m *Empty) Reset() { *m = Empty{} }
+func (m *Empty) String() string { return proto.CompactTextString(m) }
+func (*Empty) ProtoMessage() {}
+func (*Empty) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
// A block of data, to simply increase gRPC message size.
type Payload struct {
@@ -94,9 +98,10 @@ type Payload struct {
XXX_unrecognized []byte `json:"-"`
}
-func (m *Payload) Reset() { *m = Payload{} }
-func (m *Payload) String() string { return proto.CompactTextString(m) }
-func (*Payload) ProtoMessage() {}
+func (m *Payload) Reset() { *m = Payload{} }
+func (m *Payload) String() string { return proto.CompactTextString(m) }
+func (*Payload) ProtoMessage() {}
+func (*Payload) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func (m *Payload) GetType() PayloadType {
if m != nil && m.Type != nil {
@@ -129,9 +134,10 @@ type SimpleRequest struct {
XXX_unrecognized []byte `json:"-"`
}
-func (m *SimpleRequest) Reset() { *m = SimpleRequest{} }
-func (m *SimpleRequest) String() string { return proto.CompactTextString(m) }
-func (*SimpleRequest) ProtoMessage() {}
+func (m *SimpleRequest) Reset() { *m = SimpleRequest{} }
+func (m *SimpleRequest) String() string { return proto.CompactTextString(m) }
+func (*SimpleRequest) ProtoMessage() {}
+func (*SimpleRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
func (m *SimpleRequest) GetResponseType() PayloadType {
if m != nil && m.ResponseType != nil {
@@ -180,9 +186,10 @@ type SimpleResponse struct {
XXX_unrecognized []byte `json:"-"`
}
-func (m *SimpleResponse) Reset() { *m = SimpleResponse{} }
-func (m *SimpleResponse) String() string { return proto.CompactTextString(m) }
-func (*SimpleResponse) ProtoMessage() {}
+func (m *SimpleResponse) Reset() { *m = SimpleResponse{} }
+func (m *SimpleResponse) String() string { return proto.CompactTextString(m) }
+func (*SimpleResponse) ProtoMessage() {}
+func (*SimpleResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
func (m *SimpleResponse) GetPayload() *Payload {
if m != nil {
@@ -212,9 +219,10 @@ type StreamingInputCallRequest struct {
XXX_unrecognized []byte `json:"-"`
}
-func (m *StreamingInputCallRequest) Reset() { *m = StreamingInputCallRequest{} }
-func (m *StreamingInputCallRequest) String() string { return proto.CompactTextString(m) }
-func (*StreamingInputCallRequest) ProtoMessage() {}
+func (m *StreamingInputCallRequest) Reset() { *m = StreamingInputCallRequest{} }
+func (m *StreamingInputCallRequest) String() string { return proto.CompactTextString(m) }
+func (*StreamingInputCallRequest) ProtoMessage() {}
+func (*StreamingInputCallRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
func (m *StreamingInputCallRequest) GetPayload() *Payload {
if m != nil {
@@ -230,9 +238,10 @@ type StreamingInputCallResponse struct {
XXX_unrecognized []byte `json:"-"`
}
-func (m *StreamingInputCallResponse) Reset() { *m = StreamingInputCallResponse{} }
-func (m *StreamingInputCallResponse) String() string { return proto.CompactTextString(m) }
-func (*StreamingInputCallResponse) ProtoMessage() {}
+func (m *StreamingInputCallResponse) Reset() { *m = StreamingInputCallResponse{} }
+func (m *StreamingInputCallResponse) String() string { return proto.CompactTextString(m) }
+func (*StreamingInputCallResponse) ProtoMessage() {}
+func (*StreamingInputCallResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
func (m *StreamingInputCallResponse) GetAggregatedPayloadSize() int32 {
if m != nil && m.AggregatedPayloadSize != nil {
@@ -252,9 +261,10 @@ type ResponseParameters struct {
XXX_unrecognized []byte `json:"-"`
}
-func (m *ResponseParameters) Reset() { *m = ResponseParameters{} }
-func (m *ResponseParameters) String() string { return proto.CompactTextString(m) }
-func (*ResponseParameters) ProtoMessage() {}
+func (m *ResponseParameters) Reset() { *m = ResponseParameters{} }
+func (m *ResponseParameters) String() string { return proto.CompactTextString(m) }
+func (*ResponseParameters) ProtoMessage() {}
+func (*ResponseParameters) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
func (m *ResponseParameters) GetSize() int32 {
if m != nil && m.Size != nil {
@@ -284,9 +294,10 @@ type StreamingOutputCallRequest struct {
XXX_unrecognized []byte `json:"-"`
}
-func (m *StreamingOutputCallRequest) Reset() { *m = StreamingOutputCallRequest{} }
-func (m *StreamingOutputCallRequest) String() string { return proto.CompactTextString(m) }
-func (*StreamingOutputCallRequest) ProtoMessage() {}
+func (m *StreamingOutputCallRequest) Reset() { *m = StreamingOutputCallRequest{} }
+func (m *StreamingOutputCallRequest) String() string { return proto.CompactTextString(m) }
+func (*StreamingOutputCallRequest) ProtoMessage() {}
+func (*StreamingOutputCallRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} }
func (m *StreamingOutputCallRequest) GetResponseType() PayloadType {
if m != nil && m.ResponseType != nil {
@@ -316,9 +327,10 @@ type StreamingOutputCallResponse struct {
XXX_unrecognized []byte `json:"-"`
}
-func (m *StreamingOutputCallResponse) Reset() { *m = StreamingOutputCallResponse{} }
-func (m *StreamingOutputCallResponse) String() string { return proto.CompactTextString(m) }
-func (*StreamingOutputCallResponse) ProtoMessage() {}
+func (m *StreamingOutputCallResponse) Reset() { *m = StreamingOutputCallResponse{} }
+func (m *StreamingOutputCallResponse) String() string { return proto.CompactTextString(m) }
+func (*StreamingOutputCallResponse) ProtoMessage() {}
+func (*StreamingOutputCallResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} }
func (m *StreamingOutputCallResponse) GetPayload() *Payload {
if m != nil {
@@ -328,9 +340,22 @@ func (m *StreamingOutputCallResponse) GetPayload() *Payload {
}
func init() {
+ proto.RegisterType((*Empty)(nil), "grpc.testing.Empty")
+ proto.RegisterType((*Payload)(nil), "grpc.testing.Payload")
+ proto.RegisterType((*SimpleRequest)(nil), "grpc.testing.SimpleRequest")
+ proto.RegisterType((*SimpleResponse)(nil), "grpc.testing.SimpleResponse")
+ proto.RegisterType((*StreamingInputCallRequest)(nil), "grpc.testing.StreamingInputCallRequest")
+ proto.RegisterType((*StreamingInputCallResponse)(nil), "grpc.testing.StreamingInputCallResponse")
+ proto.RegisterType((*ResponseParameters)(nil), "grpc.testing.ResponseParameters")
+ proto.RegisterType((*StreamingOutputCallRequest)(nil), "grpc.testing.StreamingOutputCallRequest")
+ proto.RegisterType((*StreamingOutputCallResponse)(nil), "grpc.testing.StreamingOutputCallResponse")
proto.RegisterEnum("grpc.testing.PayloadType", PayloadType_name, PayloadType_value)
}
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConn
+
// Client API for TestService service
type TestServiceClient interface {
@@ -700,3 +725,43 @@ var _TestService_serviceDesc = grpc.ServiceDesc{
},
},
}
+
+var fileDescriptor0 = []byte{
+ // 567 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xbc, 0x54, 0x51, 0x6f, 0xd2, 0x50,
+ 0x14, 0xb6, 0x03, 0x64, 0x1c, 0x58, 0x43, 0x0e, 0x59, 0x64, 0x9d, 0x89, 0x4b, 0x7d, 0xb0, 0x9a,
+ 0x88, 0x86, 0x44, 0x1f, 0x35, 0x73, 0x63, 0x71, 0x09, 0x03, 0x6c, 0xe1, 0x99, 0x5c, 0xe1, 0x0e,
+ 0x9b, 0x94, 0xb6, 0xb6, 0xb7, 0x46, 0x7c, 0xf0, 0x8f, 0xf9, 0x67, 0xfc, 0x11, 0xfe, 0x00, 0xef,
+ 0xbd, 0x6d, 0xa1, 0x40, 0x17, 0x99, 0xc6, 0xbd, 0xb5, 0xdf, 0xf9, 0xce, 0x77, 0xbe, 0xef, 0x9e,
+ 0xdb, 0x02, 0x30, 0x1a, 0xb2, 0x96, 0x1f, 0x78, 0xcc, 0xc3, 0xda, 0x2c, 0xf0, 0x27, 0x2d, 0x01,
+ 0xd8, 0xee, 0x4c, 0x2f, 0x43, 0xa9, 0x33, 0xf7, 0xd9, 0x42, 0xef, 0x42, 0x79, 0x40, 0x16, 0x8e,
+ 0x47, 0xa6, 0xf8, 0x1c, 0x8a, 0x6c, 0xe1, 0xd3, 0xa6, 0x72, 0xa2, 0x18, 0x6a, 0xfb, 0xa8, 0x95,
+ 0x6d, 0x68, 0x25, 0xa4, 0x21, 0x27, 0x98, 0x92, 0x86, 0x08, 0xc5, 0x8f, 0xde, 0x74, 0xd1, 0xdc,
+ 0xe3, 0xf4, 0x9a, 0x29, 0x9f, 0xf5, 0x5f, 0x0a, 0x1c, 0x58, 0xf6, 0xdc, 0x77, 0xa8, 0x49, 0x3f,
+ 0x47, 0xbc, 0x15, 0xdf, 0xc0, 0x41, 0x40, 0x43, 0xdf, 0x73, 0x43, 0x3a, 0xde, 0x4d, 0xbd, 0x96,
+ 0xf2, 0xc5, 0x1b, 0x3e, 0xce, 0xf4, 0x87, 0xf6, 0x37, 0x2a, 0xc7, 0x95, 0x56, 0x24, 0x8b, 0x63,
+ 0xf8, 0x02, 0xca, 0x7e, 0xac, 0xd0, 0x2c, 0xf0, 0x72, 0xb5, 0x7d, 0x98, 0x2b, 0x6f, 0xa6, 0x2c,
+ 0xa1, 0x7a, 0x6d, 0x3b, 0xce, 0x38, 0x0a, 0x69, 0xe0, 0x92, 0x39, 0x6d, 0x16, 0x79, 0xdb, 0xbe,
+ 0x59, 0x13, 0xe0, 0x28, 0xc1, 0xd0, 0x80, 0xba, 0x24, 0x79, 0x24, 0x62, 0x9f, 0xc6, 0xe1, 0xc4,
+ 0xe3, 0xee, 0x4b, 0x92, 0xa7, 0x0a, 0xbc, 0x2f, 0x60, 0x4b, 0xa0, 0xfa, 0x77, 0x50, 0xd3, 0xd4,
+ 0xb1, 0xab, 0xac, 0x23, 0x65, 0x27, 0x47, 0x1a, 0xec, 0x2f, 0xcd, 0x88, 0x88, 0x15, 0x73, 0xf9,
+ 0x8e, 0x8f, 0xa0, 0x9a, 0xf5, 0x50, 0x90, 0x65, 0xf0, 0x56, 0xf3, 0xbb, 0x70, 0x64, 0xb1, 0x80,
+ 0x92, 0x39, 0x97, 0xbe, 0x74, 0xfd, 0x88, 0x9d, 0x11, 0xc7, 0x49, 0x37, 0x70, 0x5b, 0x2b, 0xfa,
+ 0x10, 0xb4, 0x3c, 0xb5, 0x24, 0xd9, 0x6b, 0x78, 0x40, 0x66, 0xb3, 0x80, 0xce, 0x08, 0xa3, 0xd3,
+ 0x71, 0xd2, 0x13, 0xaf, 0x46, 0x91, 0xab, 0x39, 0x5c, 0x95, 0x13, 0x69, 0xb1, 0x23, 0xfd, 0x12,
+ 0x30, 0xd5, 0x18, 0x90, 0x80, 0xc7, 0x62, 0x34, 0x08, 0xc5, 0x25, 0xca, 0xb4, 0xca, 0x67, 0x11,
+ 0xd7, 0x76, 0x79, 0xf5, 0x0b, 0x11, 0x0b, 0x4a, 0x16, 0x0e, 0x29, 0x34, 0x0a, 0xf5, 0x9f, 0x4a,
+ 0xc6, 0x61, 0x3f, 0x62, 0x1b, 0x81, 0xff, 0xf5, 0xca, 0x7d, 0x80, 0xc6, 0xb2, 0xdf, 0x5f, 0x5a,
+ 0xe5, 0x3e, 0x0a, 0xfc, 0xf0, 0x4e, 0xd6, 0x55, 0xb6, 0x23, 0x99, 0x18, 0x6c, 0xc7, 0xbc, 0xed,
+ 0x05, 0xd5, 0x7b, 0x70, 0x9c, 0x9b, 0xf0, 0x2f, 0xaf, 0xd7, 0xb3, 0xb7, 0x50, 0xcd, 0x04, 0xc6,
+ 0x3a, 0xd4, 0xce, 0xfa, 0x57, 0x03, 0xb3, 0x63, 0x59, 0xa7, 0xef, 0xba, 0x9d, 0xfa, 0x3d, 0xbe,
+ 0x08, 0x75, 0xd4, 0x5b, 0xc3, 0x14, 0x04, 0xb8, 0x6f, 0x9e, 0xf6, 0xce, 0xfb, 0x57, 0xf5, 0xbd,
+ 0xf6, 0x8f, 0x22, 0x54, 0x87, 0x5c, 0xdd, 0xe2, 0x4b, 0xb0, 0x27, 0x14, 0x5f, 0x41, 0x45, 0xfe,
+ 0x40, 0x84, 0x2d, 0x6c, 0xac, 0x4f, 0x97, 0x05, 0x2d, 0x0f, 0xc4, 0x0b, 0xa8, 0x8c, 0x5c, 0x12,
+ 0xc4, 0x6d, 0xc7, 0xeb, 0x8c, 0xb5, 0x1f, 0x87, 0xf6, 0x30, 0xbf, 0x98, 0x1c, 0x80, 0x03, 0x8d,
+ 0x9c, 0xf3, 0x41, 0x63, 0xa3, 0xe9, 0xc6, 0x4b, 0xa2, 0x3d, 0xdd, 0x81, 0x19, 0xcf, 0x7a, 0xa9,
+ 0xa0, 0x0d, 0xb8, 0xfd, 0x45, 0xe0, 0x93, 0x1b, 0x24, 0x36, 0xbf, 0x40, 0xcd, 0xf8, 0x33, 0x31,
+ 0x1e, 0x65, 0x88, 0x51, 0xea, 0x45, 0xe4, 0x38, 0xe7, 0x11, 0x4f, 0xfb, 0xf5, 0xbf, 0x65, 0x32,
+ 0x14, 0x99, 0x4a, 0x7d, 0x4f, 0x9c, 0xeb, 0x3b, 0x18, 0xf5, 0x3b, 0x00, 0x00, 0xff, 0xff, 0x4c,
+ 0x41, 0xfe, 0xb6, 0x89, 0x06, 0x00, 0x00,
+}
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/interop/server/testdata/ca.pem b/Godeps/_workspace/src/google.golang.org/grpc/interop/server/testdata/ca.pem
deleted file mode 100644
index 6c8511a73c6..00000000000
--- a/Godeps/_workspace/src/google.golang.org/grpc/interop/server/testdata/ca.pem
+++ /dev/null
@@ -1,15 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV
-BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
-aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla
-Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0
-YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT
-BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7
-+L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu
-g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd
-Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV
-HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau
-sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m
-oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG
-Dfcog5wrJytaQ6UA0wE=
------END CERTIFICATE-----
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/interop/server/testdata/server1.key b/Godeps/_workspace/src/google.golang.org/grpc/interop/server/testdata/server1.key
deleted file mode 100644
index 143a5b87658..00000000000
--- a/Godeps/_workspace/src/google.golang.org/grpc/interop/server/testdata/server1.key
+++ /dev/null
@@ -1,16 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOHDFScoLCVJpYDD
-M4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1BgzkWF+slf
-3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd9N8YwbBY
-AckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAECgYAn7qGnM2vbjJNBm0VZCkOkTIWm
-V10okw7EPJrdL2mkre9NasghNXbE1y5zDshx5Nt3KsazKOxTT8d0Jwh/3KbaN+YY
-tTCbKGW0pXDRBhwUHRcuRzScjli8Rih5UOCiZkhefUTcRb6xIhZJuQy71tjaSy0p
-dHZRmYyBYO2YEQ8xoQJBAPrJPhMBkzmEYFtyIEqAxQ/o/A6E+E4w8i+KM7nQCK7q
-K4JXzyXVAjLfyBZWHGM2uro/fjqPggGD6QH1qXCkI4MCQQDmdKeb2TrKRh5BY1LR
-81aJGKcJ2XbcDu6wMZK4oqWbTX2KiYn9GB0woM6nSr/Y6iy1u145YzYxEV/iMwff
-DJULAkB8B2MnyzOg0pNFJqBJuH29bKCcHa8gHJzqXhNO5lAlEbMK95p/P2Wi+4Hd
-aiEIAF1BF326QJcvYKmwSmrORp85AkAlSNxRJ50OWrfMZnBgzVjDx3xG6KsFQVk2
-ol6VhqL6dFgKUORFUWBvnKSyhjJxurlPEahV6oo6+A+mPhFY8eUvAkAZQyTdupP3
-XEFQKctGz+9+gKkemDp7LBBMEMBXrGTLPhpEfcjv/7KPdnFHYmhYeBTBnuVmTVWe
-F98XJ7tIFfJq
------END PRIVATE KEY-----
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/interop/server/testdata/server1.pem b/Godeps/_workspace/src/google.golang.org/grpc/interop/server/testdata/server1.pem
deleted file mode 100644
index f3d43fcc5be..00000000000
--- a/Godeps/_workspace/src/google.golang.org/grpc/interop/server/testdata/server1.pem
+++ /dev/null
@@ -1,16 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICnDCCAgWgAwIBAgIBBzANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJBVTET
-MBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ
-dHkgTHRkMQ8wDQYDVQQDEwZ0ZXN0Y2EwHhcNMTUxMTA0MDIyMDI0WhcNMjUxMTAx
-MDIyMDI0WjBlMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV
-BAcTB0NoaWNhZ28xFTATBgNVBAoTDEV4YW1wbGUsIENvLjEaMBgGA1UEAxQRKi50
-ZXN0Lmdvb2dsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOHDFSco
-LCVJpYDDM4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1Bg
-zkWF+slf3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd
-9N8YwbBYAckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAGjazBpMAkGA1UdEwQCMAAw
-CwYDVR0PBAQDAgXgME8GA1UdEQRIMEaCECoudGVzdC5nb29nbGUuZnKCGHdhdGVy
-em9vaS50ZXN0Lmdvb2dsZS5iZYISKi50ZXN0LnlvdXR1YmUuY29thwTAqAEDMA0G
-CSqGSIb3DQEBCwUAA4GBAJFXVifQNub1LUP4JlnX5lXNlo8FxZ2a12AFQs+bzoJ6
-hM044EDjqyxUqSbVePK0ni3w1fHQB5rY9yYC5f8G7aqqTY1QOhoUk8ZTSTRpnkTh
-y4jjdvTZeLDVBlueZUTDRmy2feY5aZIU18vFDK08dTG0A87pppuv1LNIR3loveU8
------END CERTIFICATE-----
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/metadata/metadata_test.go b/Godeps/_workspace/src/google.golang.org/grpc/metadata/metadata_test.go
deleted file mode 100644
index 02e6ba518ef..00000000000
--- a/Godeps/_workspace/src/google.golang.org/grpc/metadata/metadata_test.go
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- *
- * Copyright 2014, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-package metadata
-
-import (
- "reflect"
- "testing"
-)
-
-const binaryValue = string(128)
-
-func TestEncodeKeyValue(t *testing.T) {
- for _, test := range []struct {
- // input
- kin string
- vin string
- // output
- kout string
- vout string
- }{
- {"key", "abc", "key", "abc"},
- {"KEY", "abc", "key", "abc"},
- {"key-bin", "abc", "key-bin", "YWJj"},
- {"key-bin", binaryValue, "key-bin", "woA="},
- } {
- k, v := encodeKeyValue(test.kin, test.vin)
- if k != test.kout || !reflect.DeepEqual(v, test.vout) {
- t.Fatalf("encodeKeyValue(%q, %q) = %q, %q, want %q, %q", test.kin, test.vin, k, v, test.kout, test.vout)
- }
- }
-}
-
-func TestDecodeKeyValue(t *testing.T) {
- for _, test := range []struct {
- // input
- kin string
- vin string
- // output
- kout string
- vout string
- err error
- }{
- {"a", "abc", "a", "abc", nil},
- {"key-bin", "Zm9vAGJhcg==", "key-bin", "foo\x00bar", nil},
- {"key-bin", "woA=", "key-bin", binaryValue, nil},
- } {
- k, v, err := DecodeKeyValue(test.kin, test.vin)
- if k != test.kout || !reflect.DeepEqual(v, test.vout) || !reflect.DeepEqual(err, test.err) {
- t.Fatalf("DecodeKeyValue(%q, %q) = %q, %q, %v, want %q, %q, %v", test.kin, test.vin, k, v, err, test.kout, test.vout, test.err)
- }
- }
-}
-
-func TestPairsMD(t *testing.T) {
- for _, test := range []struct {
- // input
- kv []string
- // output
- md MD
- size int
- }{
- {[]string{}, MD{}, 0},
- {[]string{"k1", "v1", "k2-bin", binaryValue}, New(map[string]string{
- "k1": "v1",
- "k2-bin": binaryValue,
- }), 2},
- } {
- md := Pairs(test.kv...)
- if !reflect.DeepEqual(md, test.md) {
- t.Fatalf("Pairs(%v) = %v, want %v", test.kv, md, test.md)
- }
- if md.Len() != test.size {
- t.Fatalf("Pairs(%v) generates md of size %d, want %d", test.kv, md.Len(), test.size)
- }
- }
-}
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/picker_test.go b/Godeps/_workspace/src/google.golang.org/grpc/picker_test.go
deleted file mode 100644
index a7bb0206fd2..00000000000
--- a/Godeps/_workspace/src/google.golang.org/grpc/picker_test.go
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- *
- * Copyright 2014, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-package grpc
-
-import (
- "fmt"
- "math"
- "testing"
- "time"
-
- "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
- "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/naming"
-)
-
-type testWatcher struct {
- // the channel to receives name resolution updates
- update chan *naming.Update
- // the side channel to get to know how many updates in a batch
- side chan int
- // the channel to notifiy update injector that the update reading is done
- readDone chan int
-}
-
-func (w *testWatcher) Next() (updates []*naming.Update, err error) {
- n := <-w.side
- if n == 0 {
- return nil, fmt.Errorf("w.side is closed")
- }
- for i := 0; i < n; i++ {
- u := <-w.update
- if u != nil {
- updates = append(updates, u)
- }
- }
- w.readDone <- 0
- return
-}
-
-func (w *testWatcher) Close() {
-}
-
-func (w *testWatcher) inject(updates []*naming.Update) {
- w.side <- len(updates)
- for _, u := range updates {
- w.update <- u
- }
- <-w.readDone
-}
-
-type testNameResolver struct {
- w *testWatcher
- addr string
-}
-
-func (r *testNameResolver) Resolve(target string) (naming.Watcher, error) {
- r.w = &testWatcher{
- update: make(chan *naming.Update, 1),
- side: make(chan int, 1),
- readDone: make(chan int),
- }
- r.w.side <- 1
- r.w.update <- &naming.Update{
- Op: naming.Add,
- Addr: r.addr,
- }
- go func() {
- <-r.w.readDone
- }()
- return r.w, nil
-}
-
-func startServers(t *testing.T, numServers, port int, maxStreams uint32) ([]*server, *testNameResolver) {
- var servers []*server
- for i := 0; i < numServers; i++ {
- s := &server{readyChan: make(chan bool)}
- servers = append(servers, s)
- go s.start(t, port, maxStreams)
- s.wait(t, 2*time.Second)
- }
- // Point to server1
- addr := "127.0.0.1:" + servers[0].port
- return servers, &testNameResolver{
- addr: addr,
- }
-}
-
-func TestNameDiscovery(t *testing.T) {
- // Start 3 servers on 3 ports.
- servers, r := startServers(t, 3, 0, math.MaxUint32)
- cc, err := Dial("foo.bar.com", WithPicker(NewUnicastNamingPicker(r)), WithBlock(), WithInsecure(), WithCodec(testCodec{}))
- if err != nil {
- t.Fatalf("Failed to create ClientConn: %v", err)
- }
- var reply string
- if err := Invoke(context.Background(), "/foo/bar", &expectedRequest, &reply, cc); err != nil || reply != expectedResponse {
- t.Fatalf("grpc.Invoke(_, _, _, _, _) = %v, want ", err)
- }
- // Inject name resolution change to point to the second server now.
- var updates []*naming.Update
- updates = append(updates, &naming.Update{
- Op: naming.Delete,
- Addr: "127.0.0.1:" + servers[0].port,
- })
- updates = append(updates, &naming.Update{
- Op: naming.Add,
- Addr: "127.0.0.1:" + servers[1].port,
- })
- r.w.inject(updates)
- servers[0].stop()
- if err := Invoke(context.Background(), "/foo/bar", &expectedRequest, &reply, cc); err != nil || reply != expectedResponse {
- t.Fatalf("grpc.Invoke(_, _, _, _, _) = %v, want ", err)
- }
- // Add another server address (server#3) to name resolution
- updates = nil
- updates = append(updates, &naming.Update{
- Op: naming.Add,
- Addr: "127.0.0.1:" + servers[2].port,
- })
- r.w.inject(updates)
- // Stop server#2. The library should direct to server#3 automatically.
- servers[1].stop()
- if err := Invoke(context.Background(), "/foo/bar", &expectedRequest, &reply, cc); err != nil || reply != expectedResponse {
- t.Fatalf("grpc.Invoke(_, _, _, _, _) = %v, want ", err)
- }
- cc.Close()
- servers[2].stop()
-}
-
-func TestEmptyAddrs(t *testing.T) {
- servers, r := startServers(t, 1, 0, math.MaxUint32)
- cc, err := Dial("foo.bar.com", WithPicker(NewUnicastNamingPicker(r)), WithBlock(), WithInsecure(), WithCodec(testCodec{}))
- if err != nil {
- t.Fatalf("Failed to create ClientConn: %v", err)
- }
- var reply string
- if err := Invoke(context.Background(), "/foo/bar", &expectedRequest, &reply, cc); err != nil || reply != expectedResponse {
- t.Fatalf("grpc.Invoke(_, _, _, _, _) = %v, want ", err)
- }
- // Inject name resolution change to remove the server address so that there is no address
- // available after that.
- var updates []*naming.Update
- updates = append(updates, &naming.Update{
- Op: naming.Delete,
- Addr: "127.0.0.1:" + servers[0].port,
- })
- r.w.inject(updates)
- // Loop until the above updates apply.
- for {
- time.Sleep(10 * time.Millisecond)
- ctx, _ := context.WithTimeout(context.Background(), 10*time.Millisecond)
- if err := Invoke(ctx, "/foo/bar", &expectedRequest, &reply, cc); err != nil {
- break
- }
- }
- cc.Close()
- servers[0].stop()
-}
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/rpc_util.go b/Godeps/_workspace/src/google.golang.org/grpc/rpc_util.go
index 60c3ec761f1..2ee649bdfcb 100644
--- a/Godeps/_workspace/src/google.golang.org/grpc/rpc_util.go
+++ b/Godeps/_workspace/src/google.golang.org/grpc/rpc_util.go
@@ -135,12 +135,6 @@ func (d *gzipDecompressor) Type() string {
return "gzip"
}
-// CompressorGenerator defines the function generating a Compressor.
-type CompressorGenerator func() Compressor
-
-// DecompressorGenerator defines the function generating a Decompressor.
-type DecompressorGenerator func() Decompressor
-
// callInfo contains all related configuration and information about an RPC.
type callInfo struct {
failFast bool
@@ -197,30 +191,44 @@ const (
// parser reads complelete gRPC messages from the underlying reader.
type parser struct {
- s io.Reader
-}
+ // r is the underlying reader.
+ // See the comment on recvMsg for the permissible
+ // error types.
+ r io.Reader
-// recvMsg is to read a complete gRPC message from the stream. It is blocking if
-// the message has not been complete yet. It returns the message and its type,
-// EOF is returned with nil msg and 0 pf if the entire stream is done. Other
-// non-nil error is returned if something is wrong on reading.
-func (p *parser) recvMsg() (pf payloadFormat, msg []byte, err error) {
// The header of a gRPC message. Find more detail
// at http://www.grpc.io/docs/guides/wire.html.
- var buf [5]byte
+ header [5]byte
+}
- if _, err := io.ReadFull(p.s, buf[:]); err != nil {
+// recvMsg reads a complete gRPC message from the stream.
+//
+// It returns the message and its payload (compression/encoding)
+// format. The caller owns the returned msg memory.
+//
+// If there is an error, possible values are:
+// * io.EOF, when no messages remain
+// * io.ErrUnexpectedEOF
+// * of type transport.ConnectionError
+// * of type transport.StreamError
+// No other error values or types must be returned, which also means
+// that the underlying io.Reader must not return an incompatible
+// error.
+func (p *parser) recvMsg() (pf payloadFormat, msg []byte, err error) {
+ if _, err := io.ReadFull(p.r, p.header[:]); err != nil {
return 0, nil, err
}
- pf = payloadFormat(buf[0])
- length := binary.BigEndian.Uint32(buf[1:])
+ pf = payloadFormat(p.header[0])
+ length := binary.BigEndian.Uint32(p.header[1:])
if length == 0 {
return pf, nil, nil
}
+ // TODO(bradfitz,zhaoq): garbage. reuse buffer after proto decoding instead
+ // of making it for each message:
msg = make([]byte, int(length))
- if _, err := io.ReadFull(p.s, msg); err != nil {
+ if _, err := io.ReadFull(p.r, msg); err != nil {
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
@@ -279,7 +287,7 @@ func checkRecvPayload(pf payloadFormat, recvCompress string, dc Decompressor) er
case compressionNone:
case compressionMade:
if recvCompress == "" {
- return transport.StreamErrorf(codes.InvalidArgument, "grpc: received unexpected payload format %d", pf)
+ return transport.StreamErrorf(codes.InvalidArgument, "grpc: invalid grpc-encoding %q with compression enabled", recvCompress)
}
if dc == nil || recvCompress != dc.Type() {
return transport.StreamErrorf(codes.InvalidArgument, "grpc: Decompressor is not installed for grpc-encoding %q", recvCompress)
@@ -290,15 +298,11 @@ func checkRecvPayload(pf payloadFormat, recvCompress string, dc Decompressor) er
return nil
}
-func recv(p *parser, c Codec, s *transport.Stream, dg DecompressorGenerator, m interface{}) error {
+func recv(p *parser, c Codec, s *transport.Stream, dc Decompressor, m interface{}) error {
pf, d, err := p.recvMsg()
if err != nil {
return err
}
- var dc Decompressor
- if pf == compressionMade && dg != nil {
- dc = dg()
- }
if err := checkRecvPayload(pf, s.RecvCompress(), dc); err != nil {
return err
}
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/rpc_util_test.go b/Godeps/_workspace/src/google.golang.org/grpc/rpc_util_test.go
deleted file mode 100644
index 52ed3d7ed35..00000000000
--- a/Godeps/_workspace/src/google.golang.org/grpc/rpc_util_test.go
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- *
- * Copyright 2014, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-package grpc
-
-import (
- "bytes"
- "io"
- "reflect"
- "testing"
-
- "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
- "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
- "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codes"
- perfpb "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/test/codec_perf"
- "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport"
-)
-
-func TestSimpleParsing(t *testing.T) {
- bigMsg := bytes.Repeat([]byte{'x'}, 1<<24)
- for _, test := range []struct {
- // input
- p []byte
- // outputs
- err error
- b []byte
- pt payloadFormat
- }{
- {nil, io.EOF, nil, compressionNone},
- {[]byte{0, 0, 0, 0, 0}, nil, nil, compressionNone},
- {[]byte{0, 0, 0, 0, 1, 'a'}, nil, []byte{'a'}, compressionNone},
- {[]byte{1, 0}, io.ErrUnexpectedEOF, nil, compressionNone},
- {[]byte{0, 0, 0, 0, 10, 'a'}, io.ErrUnexpectedEOF, nil, compressionNone},
- // Check that messages with length >= 2^24 are parsed.
- {append([]byte{0, 1, 0, 0, 0}, bigMsg...), nil, bigMsg, compressionNone},
- } {
- buf := bytes.NewReader(test.p)
- parser := &parser{buf}
- pt, b, err := parser.recvMsg()
- if err != test.err || !bytes.Equal(b, test.b) || pt != test.pt {
- t.Fatalf("parser{%v}.recvMsg() = %v, %v, %v\nwant %v, %v, %v", test.p, pt, b, err, test.pt, test.b, test.err)
- }
- }
-}
-
-func TestMultipleParsing(t *testing.T) {
- // Set a byte stream consists of 3 messages with their headers.
- p := []byte{0, 0, 0, 0, 1, 'a', 0, 0, 0, 0, 2, 'b', 'c', 0, 0, 0, 0, 1, 'd'}
- b := bytes.NewReader(p)
- parser := &parser{b}
-
- wantRecvs := []struct {
- pt payloadFormat
- data []byte
- }{
- {compressionNone, []byte("a")},
- {compressionNone, []byte("bc")},
- {compressionNone, []byte("d")},
- }
- for i, want := range wantRecvs {
- pt, data, err := parser.recvMsg()
- if err != nil || pt != want.pt || !reflect.DeepEqual(data, want.data) {
- t.Fatalf("after %d calls, parser{%v}.recvMsg() = %v, %v, %v\nwant %v, %v, ",
- i, p, pt, data, err, want.pt, want.data)
- }
- }
-
- pt, data, err := parser.recvMsg()
- if err != io.EOF {
- t.Fatalf("after %d recvMsgs calls, parser{%v}.recvMsg() = %v, %v, %v\nwant _, _, %v",
- len(wantRecvs), p, pt, data, err, io.EOF)
- }
-}
-
-func TestEncode(t *testing.T) {
- for _, test := range []struct {
- // input
- msg proto.Message
- cp Compressor
- // outputs
- b []byte
- err error
- }{
- {nil, nil, []byte{0, 0, 0, 0, 0}, nil},
- } {
- b, err := encode(protoCodec{}, test.msg, nil, nil)
- if err != test.err || !bytes.Equal(b, test.b) {
- t.Fatalf("encode(_, _, %v, _) = %v, %v\nwant %v, %v", test.cp, b, err, test.b, test.err)
- }
- }
-}
-
-func TestCompress(t *testing.T) {
- for _, test := range []struct {
- // input
- data []byte
- cp Compressor
- dc Decompressor
- // outputs
- err error
- }{
- {make([]byte, 1024), &gzipCompressor{}, &gzipDecompressor{}, nil},
- } {
- b := new(bytes.Buffer)
- if err := test.cp.Do(b, test.data); err != test.err {
- t.Fatalf("Compressor.Do(_, %v) = %v, want %v", test.data, err, test.err)
- }
- if b.Len() >= len(test.data) {
- t.Fatalf("The compressor fails to compress data.")
- }
- if p, err := test.dc.Do(b); err != nil || !bytes.Equal(test.data, p) {
- t.Fatalf("Decompressor.Do(%v) = %v, %v, want %v, ", b, p, err, test.data)
- }
- }
-}
-
-func TestToRPCErr(t *testing.T) {
- for _, test := range []struct {
- // input
- errIn error
- // outputs
- errOut error
- }{
- {transport.StreamErrorf(codes.Unknown, ""), Errorf(codes.Unknown, "")},
- {transport.ErrConnClosing, Errorf(codes.Internal, transport.ErrConnClosing.Desc)},
- } {
- err := toRPCErr(test.errIn)
- if err != test.errOut {
- t.Fatalf("toRPCErr{%v} = %v \nwant %v", test.errIn, err, test.errOut)
- }
- }
-}
-
-func TestContextErr(t *testing.T) {
- for _, test := range []struct {
- // input
- errIn error
- // outputs
- errOut transport.StreamError
- }{
- {context.DeadlineExceeded, transport.StreamErrorf(codes.DeadlineExceeded, "%v", context.DeadlineExceeded)},
- {context.Canceled, transport.StreamErrorf(codes.Canceled, "%v", context.Canceled)},
- } {
- err := transport.ContextErr(test.errIn)
- if err != test.errOut {
- t.Fatalf("ContextErr{%v} = %v \nwant %v", test.errIn, err, test.errOut)
- }
- }
-}
-
-// bmEncode benchmarks encoding a Protocol Buffer message containing mSize
-// bytes.
-func bmEncode(b *testing.B, mSize int) {
- msg := &perfpb.Buffer{Body: make([]byte, mSize)}
- encoded, _ := encode(protoCodec{}, msg, nil, nil)
- encodedSz := int64(len(encoded))
- b.ReportAllocs()
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- encode(protoCodec{}, msg, nil, nil)
- }
- b.SetBytes(encodedSz)
-}
-
-func BenchmarkEncode1B(b *testing.B) {
- bmEncode(b, 1)
-}
-
-func BenchmarkEncode1KiB(b *testing.B) {
- bmEncode(b, 1024)
-}
-
-func BenchmarkEncode8KiB(b *testing.B) {
- bmEncode(b, 8*1024)
-}
-
-func BenchmarkEncode64KiB(b *testing.B) {
- bmEncode(b, 64*1024)
-}
-
-func BenchmarkEncode512KiB(b *testing.B) {
- bmEncode(b, 512*1024)
-}
-
-func BenchmarkEncode1MiB(b *testing.B) {
- bmEncode(b, 1024*1024)
-}
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/server.go b/Godeps/_workspace/src/google.golang.org/grpc/server.go
index 95a80676e52..12fb09cc4ea 100644
--- a/Godeps/_workspace/src/google.golang.org/grpc/server.go
+++ b/Godeps/_workspace/src/google.golang.org/grpc/server.go
@@ -39,6 +39,7 @@ import (
"fmt"
"io"
"net"
+ "net/http"
"reflect"
"runtime"
"strings"
@@ -46,6 +47,7 @@ import (
"time"
"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/http2"
"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/trace"
"github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codes"
"github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/credentials"
@@ -82,10 +84,11 @@ type service struct {
// Server is a gRPC server to serve RPC requests.
type Server struct {
- opts options
- mu sync.Mutex
+ opts options
+
+ mu sync.Mutex // guards following
lis map[net.Listener]bool
- conns map[transport.ServerTransport]bool
+ conns map[io.Closer]bool
m map[string]*service // service name -> service info
events trace.EventLog
}
@@ -93,9 +96,10 @@ type Server struct {
type options struct {
creds credentials.Credentials
codec Codec
- cg CompressorGenerator
- dg DecompressorGenerator
+ cp Compressor
+ dc Decompressor
maxConcurrentStreams uint32
+ useHandlerImpl bool // use http.Handler-based server
}
// A ServerOption sets options.
@@ -108,15 +112,15 @@ func CustomCodec(codec Codec) ServerOption {
}
}
-func CompressON(f CompressorGenerator) ServerOption {
+func RPCCompressor(cp Compressor) ServerOption {
return func(o *options) {
- o.cg = f
+ o.cp = cp
}
}
-func DecompressON(f DecompressorGenerator) ServerOption {
+func RPCDecompressor(dc Decompressor) ServerOption {
return func(o *options) {
- o.dg = f
+ o.dc = dc
}
}
@@ -149,7 +153,7 @@ func NewServer(opt ...ServerOption) *Server {
s := &Server{
lis: make(map[net.Listener]bool),
opts: opts,
- conns: make(map[transport.ServerTransport]bool),
+ conns: make(map[io.Closer]bool),
m: make(map[string]*service),
}
if EnableTracing {
@@ -216,9 +220,17 @@ var (
ErrServerStopped = errors.New("grpc: the server has been stopped")
)
+func (s *Server) useTransportAuthenticator(rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
+ creds, ok := s.opts.creds.(credentials.TransportAuthenticator)
+ if !ok {
+ return rawConn, nil, nil
+ }
+ return creds.ServerHandshake(rawConn)
+}
+
// Serve accepts incoming connections on the listener lis, creating a new
// ServerTransport and service goroutine for each. The service goroutines
-// read gRPC request and then call the registered handlers to reply to them.
+// read gRPC requests and then call the registered handlers to reply to them.
// Service returns when lis.Accept fails.
func (s *Server) Serve(lis net.Listener) error {
s.mu.Lock()
@@ -235,70 +247,171 @@ func (s *Server) Serve(lis net.Listener) error {
delete(s.lis, lis)
s.mu.Unlock()
}()
+ listenerAddr := lis.Addr()
for {
- c, err := lis.Accept()
+ rawConn, err := lis.Accept()
if err != nil {
s.mu.Lock()
s.printf("done serving; Accept = %v", err)
s.mu.Unlock()
return err
}
- var authInfo credentials.AuthInfo
- if creds, ok := s.opts.creds.(credentials.TransportAuthenticator); ok {
- var conn net.Conn
- conn, authInfo, err = creds.ServerHandshake(c)
- if err != nil {
- s.mu.Lock()
- s.errorf("ServerHandshake(%q) failed: %v", c.RemoteAddr(), err)
- s.mu.Unlock()
- grpclog.Println("grpc: Server.Serve failed to complete security handshake.")
- continue
- }
- c = conn
- }
+ // Start a new goroutine to deal with rawConn
+ // so we don't stall this Accept loop goroutine.
+ go s.handleRawConn(listenerAddr, rawConn)
+ }
+}
+
+// handleRawConn is run in its own goroutine and handles a just-accepted
+// connection that has not had any I/O performed on it yet.
+func (s *Server) handleRawConn(listenerAddr net.Addr, rawConn net.Conn) {
+ conn, authInfo, err := s.useTransportAuthenticator(rawConn)
+ if err != nil {
s.mu.Lock()
- if s.conns == nil {
- s.mu.Unlock()
- c.Close()
- return nil
- }
- st, err := transport.NewServerTransport("http2", c, s.opts.maxConcurrentStreams, authInfo)
- if err != nil {
- s.errorf("NewServerTransport(%q) failed: %v", c.RemoteAddr(), err)
- s.mu.Unlock()
- c.Close()
- grpclog.Println("grpc: Server.Serve failed to create ServerTransport: ", err)
- continue
- }
- s.conns[st] = true
+ s.errorf("ServerHandshake(%q) failed: %v", rawConn.RemoteAddr(), err)
+ s.mu.Unlock()
+ grpclog.Printf("grpc: Server.Serve failed to complete security handshake from %q: %v", rawConn.RemoteAddr(), err)
+ rawConn.Close()
+ return
+ }
+
+ s.mu.Lock()
+ if s.conns == nil {
s.mu.Unlock()
+ conn.Close()
+ return
+ }
+ s.mu.Unlock()
+
+ if s.opts.useHandlerImpl {
+ s.serveUsingHandler(listenerAddr, conn)
+ } else {
+ s.serveNewHTTP2Transport(conn, authInfo)
+ }
+}
+
+// serveNewHTTP2Transport sets up a new http/2 transport (using the
+// gRPC http2 server transport in transport/http2_server.go) and
+// serves streams on it.
+// This is run in its own goroutine (it does network I/O in
+// transport.NewServerTransport).
+func (s *Server) serveNewHTTP2Transport(c net.Conn, authInfo credentials.AuthInfo) {
+ st, err := transport.NewServerTransport("http2", c, s.opts.maxConcurrentStreams, authInfo)
+ if err != nil {
+ s.mu.Lock()
+ s.errorf("NewServerTransport(%q) failed: %v", c.RemoteAddr(), err)
+ s.mu.Unlock()
+ c.Close()
+ grpclog.Println("grpc: Server.Serve failed to create ServerTransport: ", err)
+ return
+ }
+ if !s.addConn(st) {
+ st.Close()
+ return
+ }
+ s.serveStreams(st)
+}
+func (s *Server) serveStreams(st transport.ServerTransport) {
+ defer s.removeConn(st)
+ defer st.Close()
+ var wg sync.WaitGroup
+ st.HandleStreams(func(stream *transport.Stream) {
+ wg.Add(1)
go func() {
- var wg sync.WaitGroup
- st.HandleStreams(func(stream *transport.Stream) {
- var trInfo *traceInfo
- if EnableTracing {
- trInfo = &traceInfo{
- tr: trace.New("grpc.Recv."+methodFamily(stream.Method()), stream.Method()),
- }
- trInfo.firstLine.client = false
- trInfo.firstLine.remoteAddr = st.RemoteAddr()
- stream.TraceContext(trInfo.tr)
- if dl, ok := stream.Context().Deadline(); ok {
- trInfo.firstLine.deadline = dl.Sub(time.Now())
- }
- }
- wg.Add(1)
- go func() {
- s.handleStream(st, stream, trInfo)
- wg.Done()
- }()
- })
- wg.Wait()
- s.mu.Lock()
- delete(s.conns, st)
- s.mu.Unlock()
+ defer wg.Done()
+ s.handleStream(st, stream, s.traceInfo(st, stream))
}()
+ })
+ wg.Wait()
+}
+
+var _ http.Handler = (*Server)(nil)
+
+// serveUsingHandler is called from handleRawConn when s is configured
+// to handle requests via the http.Handler interface. It sets up a
+// net/http.Server to handle the just-accepted conn. The http.Server
+// is configured to route all incoming requests (all HTTP/2 streams)
+// to ServeHTTP, which creates a new ServerTransport for each stream.
+// serveUsingHandler blocks until conn closes.
+//
+// This codepath is only used when Server.TestingUseHandlerImpl has
+// been configured. This lets the end2end tests exercise the ServeHTTP
+// method as one of the environment types.
+//
+// conn is the *tls.Conn that's already been authenticated.
+func (s *Server) serveUsingHandler(listenerAddr net.Addr, conn net.Conn) {
+ if !s.addConn(conn) {
+ conn.Close()
+ return
+ }
+ defer s.removeConn(conn)
+ connDone := make(chan struct{})
+ hs := &http.Server{
+ Handler: s,
+ ConnState: func(c net.Conn, cs http.ConnState) {
+ if cs == http.StateClosed {
+ close(connDone)
+ }
+ },
+ }
+ if err := http2.ConfigureServer(hs, &http2.Server{
+ MaxConcurrentStreams: s.opts.maxConcurrentStreams,
+ }); err != nil {
+ grpclog.Fatalf("grpc: http2.ConfigureServer: %v", err)
+ return
+ }
+ hs.Serve(&singleConnListener{addr: listenerAddr, conn: conn})
+ <-connDone
+}
+
+func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ st, err := transport.NewServerHandlerTransport(w, r)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ if !s.addConn(st) {
+ st.Close()
+ return
+ }
+ defer s.removeConn(st)
+ s.serveStreams(st)
+}
+
+// traceInfo returns a traceInfo and associates it with stream, if tracing is enabled.
+// If tracing is not enabled, it returns nil.
+func (s *Server) traceInfo(st transport.ServerTransport, stream *transport.Stream) (trInfo *traceInfo) {
+ if !EnableTracing {
+ return nil
+ }
+ trInfo = &traceInfo{
+ tr: trace.New("grpc.Recv."+methodFamily(stream.Method()), stream.Method()),
+ }
+ trInfo.firstLine.client = false
+ trInfo.firstLine.remoteAddr = st.RemoteAddr()
+ stream.TraceContext(trInfo.tr)
+ if dl, ok := stream.Context().Deadline(); ok {
+ trInfo.firstLine.deadline = dl.Sub(time.Now())
+ }
+ return trInfo
+}
+
+func (s *Server) addConn(c io.Closer) bool {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ if s.conns == nil {
+ return false
+ }
+ s.conns[c] = true
+ return true
+}
+
+func (s *Server) removeConn(c io.Closer) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ if s.conns != nil {
+ delete(s.conns, c)
}
}
@@ -333,13 +446,16 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
}
}()
}
- p := &parser{s: stream}
+ p := &parser{r: stream}
for {
pf, req, err := p.recvMsg()
if err == io.EOF {
// The entire stream is done (for unary RPC only).
return err
}
+ if err == io.ErrUnexpectedEOF {
+ err = transport.StreamError{Code: codes.Internal, Desc: "io.ErrUnexpectedEOF"}
+ }
if err != nil {
switch err := err.(type) {
case transport.ConnectionError:
@@ -354,11 +470,7 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
return err
}
- var dc Decompressor
- if pf == compressionMade && s.opts.dg != nil {
- dc = s.opts.dg()
- }
- if err := checkRecvPayload(pf, stream.RecvCompress(), dc); err != nil {
+ if err := checkRecvPayload(pf, stream.RecvCompress(), s.opts.dc); err != nil {
switch err := err.(type) {
case transport.StreamError:
if err := t.WriteStatus(stream, err.Code, err.Desc); err != nil {
@@ -377,9 +489,7 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
df := func(v interface{}) error {
if pf == compressionMade {
var err error
- req, err = dc.Do(bytes.NewReader(req))
- //req, err = ioutil.ReadAll(dc)
- //defer dc.Close()
+ req, err = s.opts.dc.Do(bytes.NewReader(req))
if err != nil {
if err := t.WriteStatus(stream, codes.Internal, err.Error()); err != nil {
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err)
@@ -421,12 +531,10 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
Last: true,
Delay: false,
}
- var cp Compressor
- if s.opts.cg != nil {
- cp = s.opts.cg()
- stream.SetSendCompress(cp.Type())
+ if s.opts.cp != nil {
+ stream.SetSendCompress(s.opts.cp.Type())
}
- if err := s.sendResponse(t, stream, reply, cp, opts); err != nil {
+ if err := s.sendResponse(t, stream, reply, s.opts.cp, opts); err != nil {
switch err := err.(type) {
case transport.ConnectionError:
// Nothing to do here.
@@ -447,21 +555,19 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
}
func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, sd *StreamDesc, trInfo *traceInfo) (err error) {
- var cp Compressor
- if s.opts.cg != nil {
- cp = s.opts.cg()
- stream.SetSendCompress(cp.Type())
+ if s.opts.cp != nil {
+ stream.SetSendCompress(s.opts.cp.Type())
}
ss := &serverStream{
t: t,
s: stream,
- p: &parser{s: stream},
+ p: &parser{r: stream},
codec: s.opts.codec,
- cp: cp,
- dg: s.opts.dg,
+ cp: s.opts.cp,
+ dc: s.opts.dc,
trInfo: trInfo,
}
- if cp != nil {
+ if ss.cp != nil {
ss.cbuf = new(bytes.Buffer)
}
if trInfo != nil {
@@ -571,8 +677,11 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Str
}
}
-// Stop stops the gRPC server. Once Stop returns, the server stops accepting
-// connection requests and closes all the connected connections.
+// Stop stops the gRPC server. It immediately closes all open
+// connections and listeners.
+// It cancels all active RPCs on the server side and the corresponding
+// pending RPCs on the client side will get notified by connection
+// errors.
func (s *Server) Stop() {
s.mu.Lock()
listeners := s.lis
@@ -580,12 +689,14 @@ func (s *Server) Stop() {
cs := s.conns
s.conns = nil
s.mu.Unlock()
+
for lis := range listeners {
lis.Close()
}
for c := range cs {
c.Close()
}
+
s.mu.Lock()
if s.events != nil {
s.events.Finish()
@@ -595,16 +706,24 @@ func (s *Server) Stop() {
}
// TestingCloseConns closes all exiting transports but keeps s.lis accepting new
-// connections. This is for test only now.
+// connections.
+// This is only for tests and is subject to removal.
func (s *Server) TestingCloseConns() {
s.mu.Lock()
for c := range s.conns {
c.Close()
+ delete(s.conns, c)
}
- s.conns = make(map[transport.ServerTransport]bool)
s.mu.Unlock()
}
+// TestingUseHandlerImpl enables the http.Handler-based server implementation.
+// It must be called before Serve and requires TLS credentials.
+// This is only for tests and is subject to removal.
+func (s *Server) TestingUseHandlerImpl() {
+ s.opts.useHandlerImpl = true
+}
+
// SendHeader sends header metadata. It may be called at most once from a unary
// RPC handler. The ctx is the RPC handler's Context or one derived from it.
func SendHeader(ctx context.Context, md metadata.MD) error {
@@ -635,3 +754,30 @@ func SetTrailer(ctx context.Context, md metadata.MD) error {
}
return stream.SetTrailer(md)
}
+
+// singleConnListener is a net.Listener that yields a single conn.
+type singleConnListener struct {
+ mu sync.Mutex
+ addr net.Addr
+ conn net.Conn // nil if done
+}
+
+func (ln *singleConnListener) Addr() net.Addr { return ln.addr }
+
+func (ln *singleConnListener) Close() error {
+ ln.mu.Lock()
+ defer ln.mu.Unlock()
+ ln.conn = nil
+ return nil
+}
+
+func (ln *singleConnListener) Accept() (net.Conn, error) {
+ ln.mu.Lock()
+ defer ln.mu.Unlock()
+ c := ln.conn
+ if c == nil {
+ return nil, io.EOF
+ }
+ ln.conn = nil
+ return c, nil
+}
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/stream.go b/Godeps/_workspace/src/google.golang.org/grpc/stream.go
index 156ffba5400..93bdb2bb97a 100644
--- a/Godeps/_workspace/src/google.golang.org/grpc/stream.go
+++ b/Godeps/_workspace/src/google.golang.org/grpc/stream.go
@@ -71,7 +71,7 @@ type Stream interface {
SendMsg(m interface{}) error
// RecvMsg blocks until it receives a message or the stream is
// done. On client side, it returns io.EOF when the stream is done. On
- // any other error, it aborts the streama nd returns an RPC status. On
+ // any other error, it aborts the stream and returns an RPC status. On
// server side, it simply returns the error to the caller.
RecvMsg(m interface{}) error
}
@@ -105,27 +105,24 @@ func NewClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth
if err != nil {
return nil, toRPCErr(err)
}
- var cp Compressor
- if cc.dopts.cg != nil {
- cp = cc.dopts.cg()
- }
// TODO(zhaoq): CallOption is omitted. Add support when it is needed.
callHdr := &transport.CallHdr{
Host: cc.authority,
Method: method,
+ Flush: desc.ServerStreams && desc.ClientStreams,
}
- if cp != nil {
- callHdr.SendCompress = cp.Type()
+ if cc.dopts.cp != nil {
+ callHdr.SendCompress = cc.dopts.cp.Type()
}
cs := &clientStream{
desc: desc,
codec: cc.dopts.codec,
- cp: cp,
- dg: cc.dopts.dg,
+ cp: cc.dopts.cp,
+ dc: cc.dopts.dc,
tracing: EnableTracing,
}
- if cp != nil {
- callHdr.SendCompress = cp.Type()
+ if cc.dopts.cp != nil {
+ callHdr.SendCompress = cc.dopts.cp.Type()
cs.cbuf = new(bytes.Buffer)
}
if cs.tracing {
@@ -144,7 +141,7 @@ func NewClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth
}
cs.t = t
cs.s = s
- cs.p = &parser{s: s}
+ cs.p = &parser{r: s}
// Listen on ctx.Done() to detect cancellation when there is no pending
// I/O operations on this stream.
go func() {
@@ -169,7 +166,7 @@ type clientStream struct {
codec Codec
cp Compressor
cbuf *bytes.Buffer
- dg DecompressorGenerator
+ dc Decompressor
tracing bool // set to EnableTracing when the clientStream is created.
@@ -207,6 +204,9 @@ func (cs *clientStream) SendMsg(m interface{}) (err error) {
cs.mu.Unlock()
}
defer func() {
+ if err != nil {
+ cs.finish(err)
+ }
if err == nil || err == io.EOF {
return
}
@@ -228,7 +228,7 @@ func (cs *clientStream) SendMsg(m interface{}) (err error) {
}
func (cs *clientStream) RecvMsg(m interface{}) (err error) {
- err = recv(cs.p, cs.codec, cs.s, cs.dg, m)
+ err = recv(cs.p, cs.codec, cs.s, cs.dc, m)
defer func() {
// err != nil indicates the termination of the stream.
if err != nil {
@@ -247,13 +247,14 @@ func (cs *clientStream) RecvMsg(m interface{}) (err error) {
return
}
// Special handling for client streaming rpc.
- err = recv(cs.p, cs.codec, cs.s, cs.dg, m)
+ err = recv(cs.p, cs.codec, cs.s, cs.dc, m)
cs.closeTransportStream(err)
if err == nil {
return toRPCErr(errors.New("grpc: client streaming protocol violation: get , want "))
}
if err == io.EOF {
if cs.s.StatusCode() == codes.OK {
+ cs.finish(err)
return nil
}
return Errorf(cs.s.StatusCode(), cs.s.StatusDesc())
@@ -275,6 +276,11 @@ func (cs *clientStream) RecvMsg(m interface{}) (err error) {
func (cs *clientStream) CloseSend() (err error) {
err = cs.t.Write(cs.s, nil, &transport.Options{Last: true})
+ defer func() {
+ if err != nil {
+ cs.finish(err)
+ }
+ }()
if err == nil || err == io.EOF {
return
}
@@ -333,7 +339,7 @@ type serverStream struct {
p *parser
codec Codec
cp Compressor
- dg DecompressorGenerator
+ dc Decompressor
cbuf *bytes.Buffer
statusCode codes.Code
statusDesc string
@@ -401,5 +407,5 @@ func (ss *serverStream) RecvMsg(m interface{}) (err error) {
ss.mu.Unlock()
}
}()
- return recv(ss.p, ss.codec, ss.s, ss.dg, m)
+ return recv(ss.p, ss.codec, ss.s, ss.dc, m)
}
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/test/codec_perf/perf.pb.go b/Godeps/_workspace/src/google.golang.org/grpc/test/codec_perf/perf.pb.go
index 8ad1a33aae7..ca2fb336caf 100644
--- a/Godeps/_workspace/src/google.golang.org/grpc/test/codec_perf/perf.pb.go
+++ b/Godeps/_workspace/src/google.golang.org/grpc/test/codec_perf/perf.pb.go
@@ -14,12 +14,18 @@ It has these top-level messages:
package codec_perf
import proto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+import fmt "fmt"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
+var _ = fmt.Errorf
var _ = math.Inf
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+const _ = proto.ProtoPackageIsVersion1
+
// Buffer is a message that contains a body of bytes that is used to exercise
// encoding and decoding overheads.
type Buffer struct {
@@ -27,9 +33,10 @@ type Buffer struct {
XXX_unrecognized []byte `json:"-"`
}
-func (m *Buffer) Reset() { *m = Buffer{} }
-func (m *Buffer) String() string { return proto.CompactTextString(m) }
-func (*Buffer) ProtoMessage() {}
+func (m *Buffer) Reset() { *m = Buffer{} }
+func (m *Buffer) String() string { return proto.CompactTextString(m) }
+func (*Buffer) ProtoMessage() {}
+func (*Buffer) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func (m *Buffer) GetBody() []byte {
if m != nil {
@@ -39,4 +46,14 @@ func (m *Buffer) GetBody() []byte {
}
func init() {
+ proto.RegisterType((*Buffer)(nil), "codec.perf.Buffer")
+}
+
+var fileDescriptor0 = []byte{
+ // 73 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0x2a, 0x48, 0x2d, 0x4a,
+ 0xd3, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x4a, 0xce, 0x4f, 0x49, 0x4d, 0xd6, 0x03, 0x89,
+ 0x28, 0xc9, 0x70, 0xb1, 0x39, 0x95, 0xa6, 0xa5, 0xa5, 0x16, 0x09, 0x09, 0x71, 0xb1, 0x24, 0xe5,
+ 0xa7, 0x54, 0x4a, 0x30, 0x2a, 0x30, 0x6a, 0xf0, 0x04, 0x81, 0xd9, 0x80, 0x00, 0x00, 0x00, 0xff,
+ 0xff, 0x3a, 0x58, 0x92, 0x53, 0x36, 0x00, 0x00, 0x00,
}
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/test/end2end_test.go b/Godeps/_workspace/src/google.golang.org/grpc/test/end2end_test.go
deleted file mode 100644
index 299c0c34d08..00000000000
--- a/Godeps/_workspace/src/google.golang.org/grpc/test/end2end_test.go
+++ /dev/null
@@ -1,1226 +0,0 @@
-/*
- *
- * Copyright 2014, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-package grpc_test
-
-import (
- "fmt"
- "io"
- "math"
- "net"
- "reflect"
- "runtime"
- "sync"
- "syscall"
- "testing"
- "time"
-
- "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
- "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
- "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc"
- "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codes"
- "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/credentials"
- "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/health"
- healthpb "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/health/grpc_health_v1alpha"
- "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/metadata"
- "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/peer"
- testpb "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/test/grpc_testing"
-)
-
-var (
- testMetadata = metadata.MD{
- "key1": []string{"value1"},
- "key2": []string{"value2"},
- }
- testAppUA = "myApp1/1.0 myApp2/0.9"
-)
-
-type testServer struct {
- security string // indicate the authentication protocol used by this server.
-}
-
-func (s *testServer) EmptyCall(ctx context.Context, in *testpb.Empty) (*testpb.Empty, error) {
- if md, ok := metadata.FromContext(ctx); ok {
- // For testing purpose, returns an error if there is attached metadata other than
- // the user agent set by the client application.
- if _, ok := md["user-agent"]; !ok {
- return nil, grpc.Errorf(codes.DataLoss, "got extra metadata")
- }
- var str []string
- for _, entry := range md["user-agent"] {
- str = append(str, "ua", entry)
- }
- grpc.SendHeader(ctx, metadata.Pairs(str...))
- }
- return new(testpb.Empty), nil
-}
-
-func newPayload(t testpb.PayloadType, size int32) (*testpb.Payload, error) {
- if size < 0 {
- return nil, fmt.Errorf("Requested a response with invalid length %d", size)
- }
- body := make([]byte, size)
- switch t {
- case testpb.PayloadType_COMPRESSABLE:
- case testpb.PayloadType_UNCOMPRESSABLE:
- return nil, fmt.Errorf("PayloadType UNCOMPRESSABLE is not supported")
- default:
- return nil, fmt.Errorf("Unsupported payload type: %d", t)
- }
- return &testpb.Payload{
- Type: t.Enum(),
- Body: body,
- }, nil
-}
-
-func (s *testServer) UnaryCall(ctx context.Context, in *testpb.SimpleRequest) (*testpb.SimpleResponse, error) {
- md, ok := metadata.FromContext(ctx)
- if ok {
- if err := grpc.SendHeader(ctx, md); err != nil {
- return nil, fmt.Errorf("grpc.SendHeader(%v, %v) = %v, want %v", ctx, md, err, nil)
- }
- grpc.SetTrailer(ctx, md)
- }
- pr, ok := peer.FromContext(ctx)
- if !ok {
- return nil, fmt.Errorf("failed to get peer from ctx")
- }
- if pr.Addr == net.Addr(nil) {
- return nil, fmt.Errorf("failed to get peer address")
- }
- if s.security != "" {
- // Check Auth info
- var authType, serverName string
- switch info := pr.AuthInfo.(type) {
- case credentials.TLSInfo:
- authType = info.AuthType()
- serverName = info.State.ServerName
- default:
- return nil, fmt.Errorf("Unknown AuthInfo type")
- }
- if authType != s.security {
- return nil, fmt.Errorf("Wrong auth type: got %q, want %q", authType, s.security)
- }
- if serverName != "x.test.youtube.com" {
- return nil, fmt.Errorf("Unknown server name %q", serverName)
- }
- }
-
- // Simulate some service delay.
- time.Sleep(time.Second)
-
- payload, err := newPayload(in.GetResponseType(), in.GetResponseSize())
- if err != nil {
- return nil, err
- }
- return &testpb.SimpleResponse{
- Payload: payload,
- }, nil
-}
-
-func (s *testServer) StreamingOutputCall(args *testpb.StreamingOutputCallRequest, stream testpb.TestService_StreamingOutputCallServer) error {
- if md, ok := metadata.FromContext(stream.Context()); ok {
- // For testing purpose, returns an error if there is attached metadata.
- if len(md) > 0 {
- return grpc.Errorf(codes.DataLoss, "got extra metadata")
- }
- }
- cs := args.GetResponseParameters()
- for _, c := range cs {
- if us := c.GetIntervalUs(); us > 0 {
- time.Sleep(time.Duration(us) * time.Microsecond)
- }
-
- payload, err := newPayload(args.GetResponseType(), c.GetSize())
- if err != nil {
- return err
- }
-
- if err := stream.Send(&testpb.StreamingOutputCallResponse{
- Payload: payload,
- }); err != nil {
- return err
- }
- }
- return nil
-}
-
-func (s *testServer) StreamingInputCall(stream testpb.TestService_StreamingInputCallServer) error {
- var sum int
- for {
- in, err := stream.Recv()
- if err == io.EOF {
- return stream.SendAndClose(&testpb.StreamingInputCallResponse{
- AggregatedPayloadSize: proto.Int32(int32(sum)),
- })
- }
- if err != nil {
- return err
- }
- p := in.GetPayload().GetBody()
- sum += len(p)
- }
-}
-
-func (s *testServer) FullDuplexCall(stream testpb.TestService_FullDuplexCallServer) error {
- md, ok := metadata.FromContext(stream.Context())
- if ok {
- if err := stream.SendHeader(md); err != nil {
- return fmt.Errorf("%v.SendHeader(%v) = %v, want %v", stream, md, err, nil)
- }
- stream.SetTrailer(md)
- }
- for {
- in, err := stream.Recv()
- if err == io.EOF {
- // read done.
- return nil
- }
- if err != nil {
- return err
- }
- cs := in.GetResponseParameters()
- for _, c := range cs {
- if us := c.GetIntervalUs(); us > 0 {
- time.Sleep(time.Duration(us) * time.Microsecond)
- }
-
- payload, err := newPayload(in.GetResponseType(), c.GetSize())
- if err != nil {
- return err
- }
-
- if err := stream.Send(&testpb.StreamingOutputCallResponse{
- Payload: payload,
- }); err != nil {
- return err
- }
- }
- }
-}
-
-func (s *testServer) HalfDuplexCall(stream testpb.TestService_HalfDuplexCallServer) error {
- var msgBuf []*testpb.StreamingOutputCallRequest
- for {
- in, err := stream.Recv()
- if err == io.EOF {
- // read done.
- break
- }
- if err != nil {
- return err
- }
- msgBuf = append(msgBuf, in)
- }
- for _, m := range msgBuf {
- cs := m.GetResponseParameters()
- for _, c := range cs {
- if us := c.GetIntervalUs(); us > 0 {
- time.Sleep(time.Duration(us) * time.Microsecond)
- }
-
- payload, err := newPayload(m.GetResponseType(), c.GetSize())
- if err != nil {
- return err
- }
-
- if err := stream.Send(&testpb.StreamingOutputCallResponse{
- Payload: payload,
- }); err != nil {
- return err
- }
- }
- }
- return nil
-}
-
-const tlsDir = "testdata/"
-
-func TestReconnectTimeout(t *testing.T) {
- lis, err := net.Listen("tcp", ":0")
- if err != nil {
- t.Fatalf("Failed to listen: %v", err)
- }
- _, port, err := net.SplitHostPort(lis.Addr().String())
- if err != nil {
- t.Fatalf("Failed to parse listener address: %v", err)
- }
- addr := "localhost:" + port
- conn, err := grpc.Dial(addr, grpc.WithTimeout(5*time.Second), grpc.WithBlock(), grpc.WithInsecure())
- if err != nil {
- t.Fatalf("Failed to dial to the server %q: %v", addr, err)
- }
- // Close unaccepted connection (i.e., conn).
- lis.Close()
- tc := testpb.NewTestServiceClient(conn)
- waitC := make(chan struct{})
- go func() {
- defer close(waitC)
- argSize := 271828
- respSize := 314159
-
- payload, err := newPayload(testpb.PayloadType_COMPRESSABLE, int32(argSize))
- if err != nil {
- t.Fatal(err)
- }
-
- req := &testpb.SimpleRequest{
- ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
- ResponseSize: proto.Int32(int32(respSize)),
- Payload: payload,
- }
- if _, err := tc.UnaryCall(context.Background(), req); err == nil {
- t.Fatalf("TestService/UnaryCall(_, _) = _, , want _, non-nil")
- }
- }()
- // Block untill reconnect times out.
- <-waitC
- if err := conn.Close(); err != grpc.ErrClientConnClosing {
- t.Fatalf("%v.Close() = %v, want %v", conn, err, grpc.ErrClientConnClosing)
- }
-}
-
-func unixDialer(addr string, timeout time.Duration) (net.Conn, error) {
- return net.DialTimeout("unix", addr, timeout)
-}
-
-type env struct {
- network string // The type of network such as tcp, unix, etc.
- dialer func(addr string, timeout time.Duration) (net.Conn, error)
- security string // The security protocol such as TLS, SSH, etc.
-}
-
-func listTestEnv() []env {
- if runtime.GOOS == "windows" {
- return []env{{"tcp", nil, ""}, {"tcp", nil, "tls"}}
- }
- return []env{{"tcp", nil, ""}, {"tcp", nil, "tls"}, {"unix", unixDialer, ""}, {"unix", unixDialer, "tls"}}
-}
-
-func serverSetUp(t *testing.T, hs *health.HealthServer, maxStream uint32, cg grpc.CompressorGenerator, dg grpc.DecompressorGenerator, e env) (s *grpc.Server, addr string) {
- sopts := []grpc.ServerOption{grpc.MaxConcurrentStreams(maxStream), grpc.CompressON(cg), grpc.DecompressON(dg)}
- la := ":0"
- switch e.network {
- case "unix":
- la = "/tmp/testsock" + fmt.Sprintf("%d", time.Now())
- syscall.Unlink(la)
- }
- lis, err := net.Listen(e.network, la)
- if err != nil {
- t.Fatalf("Failed to listen: %v", err)
- }
- if e.security == "tls" {
- creds, err := credentials.NewServerTLSFromFile(tlsDir+"server1.pem", tlsDir+"server1.key")
- if err != nil {
- t.Fatalf("Failed to generate credentials %v", err)
- }
- sopts = append(sopts, grpc.Creds(creds))
- }
- s = grpc.NewServer(sopts...)
- if hs != nil {
- healthpb.RegisterHealthServer(s, hs)
- }
- testpb.RegisterTestServiceServer(s, &testServer{security: e.security})
- go s.Serve(lis)
- addr = la
- switch e.network {
- case "unix":
- default:
- _, port, err := net.SplitHostPort(lis.Addr().String())
- if err != nil {
- t.Fatalf("Failed to parse listener address: %v", err)
- }
- addr = "localhost:" + port
- }
- return
-}
-
-func clientSetUp(t *testing.T, addr string, cg grpc.CompressorGenerator, dg grpc.DecompressorGenerator, ua string, e env) (cc *grpc.ClientConn) {
- var derr error
- if e.security == "tls" {
- creds, err := credentials.NewClientTLSFromFile(tlsDir+"ca.pem", "x.test.youtube.com")
- if err != nil {
- t.Fatalf("Failed to create credentials %v", err)
- }
- cc, derr = grpc.Dial(addr, grpc.WithTransportCredentials(creds), grpc.WithDialer(e.dialer), grpc.WithUserAgent(ua), grpc.WithCompressor(cg), grpc.WithDecompressor(dg))
- } else {
- cc, derr = grpc.Dial(addr, grpc.WithDialer(e.dialer), grpc.WithInsecure(), grpc.WithUserAgent(ua), grpc.WithCompressor(cg), grpc.WithDecompressor(dg))
- }
- if derr != nil {
- t.Fatalf("Dial(%q) = %v", addr, derr)
- }
- return
-}
-
-func tearDown(s *grpc.Server, cc *grpc.ClientConn) {
- cc.Close()
- s.Stop()
-}
-
-func TestTimeoutOnDeadServer(t *testing.T) {
- for _, e := range listTestEnv() {
- testTimeoutOnDeadServer(t, e)
- }
-}
-
-func testTimeoutOnDeadServer(t *testing.T, e env) {
- s, addr := serverSetUp(t, nil, math.MaxUint32, nil, nil, e)
- cc := clientSetUp(t, addr, nil, nil, "", e)
- tc := testpb.NewTestServiceClient(cc)
- ctx, _ := context.WithTimeout(context.Background(), time.Second)
- if _, err := cc.WaitForStateChange(ctx, grpc.Idle); err != nil {
- t.Fatalf("cc.WaitForStateChange(_, %s) = _, %v, want _, ", grpc.Idle, err)
- }
- ctx, _ = context.WithTimeout(context.Background(), time.Second)
- if _, err := cc.WaitForStateChange(ctx, grpc.Connecting); err != nil {
- t.Fatalf("cc.WaitForStateChange(_, %s) = _, %v, want _, ", grpc.Connecting, err)
- }
- if state, err := cc.State(); err != nil || state != grpc.Ready {
- t.Fatalf("cc.State() = %s, %v, want %s, ", state, err, grpc.Ready)
- }
- ctx, _ = context.WithTimeout(context.Background(), time.Second)
- if _, err := cc.WaitForStateChange(ctx, grpc.Ready); err != context.DeadlineExceeded {
- t.Fatalf("cc.WaitForStateChange(_, %s) = _, %v, want _, %v", grpc.Ready, err, context.DeadlineExceeded)
- }
- s.Stop()
- // Set -1 as the timeout to make sure if transportMonitor gets error
- // notification in time the failure path of the 1st invoke of
- // ClientConn.wait hits the deadline exceeded error.
- ctx, _ = context.WithTimeout(context.Background(), -1)
- if _, err := tc.EmptyCall(ctx, &testpb.Empty{}); grpc.Code(err) != codes.DeadlineExceeded {
- t.Fatalf("TestService/EmptyCall(%v, _) = _, error %v, want _, error code: %d", ctx, err, codes.DeadlineExceeded)
- }
- ctx, _ = context.WithTimeout(context.Background(), time.Second)
- if _, err := cc.WaitForStateChange(ctx, grpc.Ready); err != nil {
- t.Fatalf("cc.WaitForStateChange(_, %s) = _, %v, want _, ", grpc.Ready, err)
- }
- if state, err := cc.State(); err != nil || (state != grpc.Connecting && state != grpc.TransientFailure) {
- t.Fatalf("cc.State() = %s, %v, want %s or %s, ", state, err, grpc.Connecting, grpc.TransientFailure)
- }
- cc.Close()
-}
-
-func healthCheck(t time.Duration, cc *grpc.ClientConn, serviceName string) (*healthpb.HealthCheckResponse, error) {
- ctx, _ := context.WithTimeout(context.Background(), t)
- hc := healthpb.NewHealthClient(cc)
- req := &healthpb.HealthCheckRequest{
- Service: serviceName,
- }
- return hc.Check(ctx, req)
-}
-
-func TestHealthCheckOnSuccess(t *testing.T) {
- for _, e := range listTestEnv() {
- testHealthCheckOnSuccess(t, e)
- }
-}
-
-func testHealthCheckOnSuccess(t *testing.T, e env) {
- hs := health.NewHealthServer()
- hs.SetServingStatus("grpc.health.v1alpha.Health", 1)
- s, addr := serverSetUp(t, hs, math.MaxUint32, nil, nil, e)
- cc := clientSetUp(t, addr, nil, nil, "", e)
- defer tearDown(s, cc)
- if _, err := healthCheck(1*time.Second, cc, "grpc.health.v1alpha.Health"); err != nil {
- t.Fatalf("Health/Check(_, _) = _, %v, want _, ", err)
- }
-}
-
-func TestHealthCheckOnFailure(t *testing.T) {
- for _, e := range listTestEnv() {
- testHealthCheckOnFailure(t, e)
- }
-}
-
-func testHealthCheckOnFailure(t *testing.T, e env) {
- hs := health.NewHealthServer()
- hs.SetServingStatus("grpc.health.v1alpha.HealthCheck", 1)
- s, addr := serverSetUp(t, hs, math.MaxUint32, nil, nil, e)
- cc := clientSetUp(t, addr, nil, nil, "", e)
- defer tearDown(s, cc)
- if _, err := healthCheck(0*time.Second, cc, "grpc.health.v1alpha.Health"); err != grpc.Errorf(codes.DeadlineExceeded, "context deadline exceeded") {
- t.Fatalf("Health/Check(_, _) = _, %v, want _, error code %d", err, codes.DeadlineExceeded)
- }
-}
-
-func TestHealthCheckOff(t *testing.T) {
- for _, e := range listTestEnv() {
- testHealthCheckOff(t, e)
- }
-}
-
-func testHealthCheckOff(t *testing.T, e env) {
- s, addr := serverSetUp(t, nil, math.MaxUint32, nil, nil, e)
- cc := clientSetUp(t, addr, nil, nil, "", e)
- defer tearDown(s, cc)
- if _, err := healthCheck(1*time.Second, cc, ""); err != grpc.Errorf(codes.Unimplemented, "unknown service grpc.health.v1alpha.Health") {
- t.Fatalf("Health/Check(_, _) = _, %v, want _, error code %d", err, codes.Unimplemented)
- }
-}
-
-func TestHealthCheckServingStatus(t *testing.T) {
- for _, e := range listTestEnv() {
- testHealthCheckServingStatus(t, e)
- }
-}
-
-func testHealthCheckServingStatus(t *testing.T, e env) {
- hs := health.NewHealthServer()
- s, addr := serverSetUp(t, hs, math.MaxUint32, nil, nil, e)
- cc := clientSetUp(t, addr, nil, nil, "", e)
- defer tearDown(s, cc)
- out, err := healthCheck(1*time.Second, cc, "")
- if err != nil {
- t.Fatalf("Health/Check(_, _) = _, %v, want _, ", err)
- }
- if out.Status != healthpb.HealthCheckResponse_SERVING {
- t.Fatalf("Got the serving status %v, want SERVING", out.Status)
- }
- if _, err := healthCheck(1*time.Second, cc, "grpc.health.v1alpha.Health"); err != grpc.Errorf(codes.NotFound, "unknown service") {
- t.Fatalf("Health/Check(_, _) = _, %v, want _, error code %d", err, codes.NotFound)
- }
- hs.SetServingStatus("grpc.health.v1alpha.Health", healthpb.HealthCheckResponse_SERVING)
- out, err = healthCheck(1*time.Second, cc, "grpc.health.v1alpha.Health")
- if err != nil {
- t.Fatalf("Health/Check(_, _) = _, %v, want _, ", err)
- }
- if out.Status != healthpb.HealthCheckResponse_SERVING {
- t.Fatalf("Got the serving status %v, want SERVING", out.Status)
- }
- hs.SetServingStatus("grpc.health.v1alpha.Health", healthpb.HealthCheckResponse_NOT_SERVING)
- out, err = healthCheck(1*time.Second, cc, "grpc.health.v1alpha.Health")
- if err != nil {
- t.Fatalf("Health/Check(_, _) = _, %v, want _, ", err)
- }
- if out.Status != healthpb.HealthCheckResponse_NOT_SERVING {
- t.Fatalf("Got the serving status %v, want NOT_SERVING", out.Status)
- }
-
-}
-
-func TestEmptyUnaryWithUserAgent(t *testing.T) {
- for _, e := range listTestEnv() {
- testEmptyUnaryWithUserAgent(t, e)
- }
-}
-
-func testEmptyUnaryWithUserAgent(t *testing.T, e env) {
- s, addr := serverSetUp(t, nil, math.MaxUint32, nil, nil, e)
- cc := clientSetUp(t, addr, nil, nil, testAppUA, e)
- // Wait until cc is connected.
- ctx, _ := context.WithTimeout(context.Background(), time.Second)
- if _, err := cc.WaitForStateChange(ctx, grpc.Idle); err != nil {
- t.Fatalf("cc.WaitForStateChange(_, %s) = _, %v, want _, ", grpc.Idle, err)
- }
- ctx, _ = context.WithTimeout(context.Background(), time.Second)
- if _, err := cc.WaitForStateChange(ctx, grpc.Connecting); err != nil {
- t.Fatalf("cc.WaitForStateChange(_, %s) = _, %v, want _, ", grpc.Connecting, err)
- }
- if state, err := cc.State(); err != nil || state != grpc.Ready {
- t.Fatalf("cc.State() = %s, %v, want %s, ", state, err, grpc.Ready)
- }
- ctx, _ = context.WithTimeout(context.Background(), time.Second)
- if _, err := cc.WaitForStateChange(ctx, grpc.Ready); err == nil {
- t.Fatalf("cc.WaitForStateChange(_, %s) = _, , want _, %v", grpc.Ready, context.DeadlineExceeded)
- }
- tc := testpb.NewTestServiceClient(cc)
- var header metadata.MD
- reply, err := tc.EmptyCall(context.Background(), &testpb.Empty{}, grpc.Header(&header))
- if err != nil || !proto.Equal(&testpb.Empty{}, reply) {
- t.Fatalf("TestService/EmptyCall(_, _) = %v, %v, want %v, ", reply, err, &testpb.Empty{})
- }
- if v, ok := header["ua"]; !ok || v[0] != testAppUA {
- t.Fatalf("header[\"ua\"] = %q, %t, want %q, true", v, ok, testAppUA)
- }
- tearDown(s, cc)
- ctx, _ = context.WithTimeout(context.Background(), 5*time.Second)
- if _, err := cc.WaitForStateChange(ctx, grpc.Ready); err != nil {
- t.Fatalf("cc.WaitForStateChange(_, %s) = _, %v, want _, ", grpc.Ready, err)
- }
- if state, err := cc.State(); err != nil || state != grpc.Shutdown {
- t.Fatalf("cc.State() = %s, %v, want %s, ", state, err, grpc.Shutdown)
- }
-}
-
-func TestFailedEmptyUnary(t *testing.T) {
- for _, e := range listTestEnv() {
- testFailedEmptyUnary(t, e)
- }
-}
-
-func testFailedEmptyUnary(t *testing.T, e env) {
- s, addr := serverSetUp(t, nil, math.MaxUint32, nil, nil, e)
- cc := clientSetUp(t, addr, nil, nil, "", e)
- tc := testpb.NewTestServiceClient(cc)
- defer tearDown(s, cc)
- ctx := metadata.NewContext(context.Background(), testMetadata)
- if _, err := tc.EmptyCall(ctx, &testpb.Empty{}); err != grpc.Errorf(codes.DataLoss, "got extra metadata") {
- t.Fatalf("TestService/EmptyCall(_, _) = _, %v, want _, %v", err, grpc.Errorf(codes.DataLoss, "got extra metadata"))
- }
-}
-
-func TestLargeUnary(t *testing.T) {
- for _, e := range listTestEnv() {
- testLargeUnary(t, e)
- }
-}
-
-func testLargeUnary(t *testing.T, e env) {
- s, addr := serverSetUp(t, nil, math.MaxUint32, nil, nil, e)
- cc := clientSetUp(t, addr, nil, nil, "", e)
- tc := testpb.NewTestServiceClient(cc)
- defer tearDown(s, cc)
- argSize := 271828
- respSize := 314159
-
- payload, err := newPayload(testpb.PayloadType_COMPRESSABLE, int32(argSize))
- if err != nil {
- t.Fatal(err)
- }
-
- req := &testpb.SimpleRequest{
- ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
- ResponseSize: proto.Int32(int32(respSize)),
- Payload: payload,
- }
- reply, err := tc.UnaryCall(context.Background(), req)
- if err != nil {
- t.Fatalf("TestService/UnaryCall(_, _) = _, %v, want _, ", err)
- }
- pt := reply.GetPayload().GetType()
- ps := len(reply.GetPayload().GetBody())
- if pt != testpb.PayloadType_COMPRESSABLE || ps != respSize {
- t.Fatalf("Got the reply with type %d len %d; want %d, %d", pt, ps, testpb.PayloadType_COMPRESSABLE, respSize)
- }
-}
-
-func TestMetadataUnaryRPC(t *testing.T) {
- for _, e := range listTestEnv() {
- testMetadataUnaryRPC(t, e)
- }
-}
-
-func testMetadataUnaryRPC(t *testing.T, e env) {
- s, addr := serverSetUp(t, nil, math.MaxUint32, nil, nil, e)
- cc := clientSetUp(t, addr, nil, nil, "", e)
- tc := testpb.NewTestServiceClient(cc)
- defer tearDown(s, cc)
- argSize := 2718
- respSize := 314
-
- payload, err := newPayload(testpb.PayloadType_COMPRESSABLE, int32(argSize))
- if err != nil {
- t.Fatal(err)
- }
-
- req := &testpb.SimpleRequest{
- ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
- ResponseSize: proto.Int32(int32(respSize)),
- Payload: payload,
- }
- var header, trailer metadata.MD
- ctx := metadata.NewContext(context.Background(), testMetadata)
- if _, err := tc.UnaryCall(ctx, req, grpc.Header(&header), grpc.Trailer(&trailer)); err != nil {
- t.Fatalf("TestService.UnaryCall(%v, _, _, _) = _, %v; want _, ", ctx, err)
- }
- if !reflect.DeepEqual(testMetadata, header) {
- t.Fatalf("Received header metadata %v, want %v", header, testMetadata)
- }
- if !reflect.DeepEqual(testMetadata, trailer) {
- t.Fatalf("Received trailer metadata %v, want %v", trailer, testMetadata)
- }
-}
-
-func performOneRPC(t *testing.T, tc testpb.TestServiceClient, wg *sync.WaitGroup) {
- argSize := 2718
- respSize := 314
-
- payload, err := newPayload(testpb.PayloadType_COMPRESSABLE, int32(argSize))
- if err != nil {
- t.Fatal(err)
- }
-
- req := &testpb.SimpleRequest{
- ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
- ResponseSize: proto.Int32(int32(respSize)),
- Payload: payload,
- }
- reply, err := tc.UnaryCall(context.Background(), req)
- if err != nil {
- t.Fatalf("TestService/UnaryCall(_, _) = _, %v, want _, ", err)
- }
- pt := reply.GetPayload().GetType()
- ps := len(reply.GetPayload().GetBody())
- if pt != testpb.PayloadType_COMPRESSABLE || ps != respSize {
- t.Fatalf("Got the reply with type %d len %d; want %d, %d", pt, ps, testpb.PayloadType_COMPRESSABLE, respSize)
- }
- wg.Done()
-}
-
-func TestRetry(t *testing.T) {
- for _, e := range listTestEnv() {
- testRetry(t, e)
- }
-}
-
-// This test mimics a user who sends 1000 RPCs concurrently on a faulty transport.
-// TODO(zhaoq): Refactor to make this clearer and add more cases to test racy
-// and error-prone paths.
-func testRetry(t *testing.T, e env) {
- s, addr := serverSetUp(t, nil, math.MaxUint32, nil, nil, e)
- cc := clientSetUp(t, addr, nil, nil, "", e)
- tc := testpb.NewTestServiceClient(cc)
- defer tearDown(s, cc)
- var wg sync.WaitGroup
- wg.Add(1)
- go func() {
- time.Sleep(1 * time.Second)
- // The server shuts down the network connection to make a
- // transport error which will be detected by the client side
- // code.
- s.TestingCloseConns()
- wg.Done()
- }()
- // All these RPCs should succeed eventually.
- for i := 0; i < 1000; i++ {
- time.Sleep(2 * time.Millisecond)
- wg.Add(1)
- go performOneRPC(t, tc, &wg)
- }
- wg.Wait()
-}
-
-func TestRPCTimeout(t *testing.T) {
- for _, e := range listTestEnv() {
- testRPCTimeout(t, e)
- }
-}
-
-// TODO(zhaoq): Have a better test coverage of timeout and cancellation mechanism.
-func testRPCTimeout(t *testing.T, e env) {
- s, addr := serverSetUp(t, nil, math.MaxUint32, nil, nil, e)
- cc := clientSetUp(t, addr, nil, nil, "", e)
- tc := testpb.NewTestServiceClient(cc)
- defer tearDown(s, cc)
- argSize := 2718
- respSize := 314
-
- payload, err := newPayload(testpb.PayloadType_COMPRESSABLE, int32(argSize))
- if err != nil {
- t.Fatal(err)
- }
-
- req := &testpb.SimpleRequest{
- ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
- ResponseSize: proto.Int32(int32(respSize)),
- Payload: payload,
- }
- for i := -1; i <= 10; i++ {
- ctx, _ := context.WithTimeout(context.Background(), time.Duration(i)*time.Millisecond)
- reply, err := tc.UnaryCall(ctx, req)
- if grpc.Code(err) != codes.DeadlineExceeded {
- t.Fatalf(`TestService/UnaryCallv(_, _) = %v, %v; want , error code: %d`, reply, err, codes.DeadlineExceeded)
- }
- }
-}
-
-func TestCancel(t *testing.T) {
- for _, e := range listTestEnv() {
- testCancel(t, e)
- }
-}
-
-func testCancel(t *testing.T, e env) {
- s, addr := serverSetUp(t, nil, math.MaxUint32, nil, nil, e)
- cc := clientSetUp(t, addr, nil, nil, "", e)
- tc := testpb.NewTestServiceClient(cc)
- defer tearDown(s, cc)
- argSize := 2718
- respSize := 314
-
- payload, err := newPayload(testpb.PayloadType_COMPRESSABLE, int32(argSize))
- if err != nil {
- t.Fatal(err)
- }
-
- req := &testpb.SimpleRequest{
- ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
- ResponseSize: proto.Int32(int32(respSize)),
- Payload: payload,
- }
- ctx, cancel := context.WithCancel(context.Background())
- time.AfterFunc(1*time.Millisecond, cancel)
- reply, err := tc.UnaryCall(ctx, req)
- if grpc.Code(err) != codes.Canceled {
- t.Fatalf(`TestService/UnaryCall(_, _) = %v, %v; want , error code: %d`, reply, err, codes.Canceled)
- }
-}
-
-func TestCancelNoIO(t *testing.T) {
- for _, e := range listTestEnv() {
- testCancelNoIO(t, e)
- }
-}
-
-func testCancelNoIO(t *testing.T, e env) {
- // Only allows 1 live stream per server transport.
- s, addr := serverSetUp(t, nil, 1, nil, nil, e)
- cc := clientSetUp(t, addr, nil, nil, "", e)
- tc := testpb.NewTestServiceClient(cc)
- defer tearDown(s, cc)
- ctx, cancel := context.WithCancel(context.Background())
- _, err := tc.StreamingInputCall(ctx)
- if err != nil {
- t.Fatalf("%v.StreamingInputCall(_) = _, %v, want _, ", tc, err)
- }
- // Loop until receiving the new max stream setting from the server.
- for {
- ctx, _ := context.WithTimeout(context.Background(), time.Second)
- _, err := tc.StreamingInputCall(ctx)
- if err == nil {
- time.Sleep(time.Second)
- continue
- }
- if grpc.Code(err) == codes.DeadlineExceeded {
- break
- }
- t.Fatalf("%v.StreamingInputCall(_) = _, %v, want _, %d", tc, err, codes.DeadlineExceeded)
- }
- // If there are any RPCs slipping before the client receives the max streams setting,
- // let them be expired.
- time.Sleep(2 * time.Second)
- ch := make(chan struct{})
- go func() {
- defer close(ch)
- // This should be blocked until the 1st is canceled.
- ctx, _ := context.WithTimeout(context.Background(), 2*time.Second)
- if _, err := tc.StreamingInputCall(ctx); err != nil {
- t.Errorf("%v.StreamingInputCall(_) = _, %v, want _, ", tc, err)
- }
- }()
- cancel()
- <-ch
-}
-
-// The following tests the gRPC streaming RPC implementations.
-// TODO(zhaoq): Have better coverage on error cases.
-var (
- reqSizes = []int{27182, 8, 1828, 45904}
- respSizes = []int{31415, 9, 2653, 58979}
-)
-
-func TestPingPong(t *testing.T) {
- for _, e := range listTestEnv() {
- testPingPong(t, e)
- }
-}
-
-func testPingPong(t *testing.T, e env) {
- s, addr := serverSetUp(t, nil, math.MaxUint32, nil, nil, e)
- cc := clientSetUp(t, addr, nil, nil, "", e)
- tc := testpb.NewTestServiceClient(cc)
- defer tearDown(s, cc)
- stream, err := tc.FullDuplexCall(context.Background())
- if err != nil {
- t.Fatalf("%v.FullDuplexCall(_) = _, %v, want ", tc, err)
- }
- var index int
- for index < len(reqSizes) {
- respParam := []*testpb.ResponseParameters{
- {
- Size: proto.Int32(int32(respSizes[index])),
- },
- }
-
- payload, err := newPayload(testpb.PayloadType_COMPRESSABLE, int32(reqSizes[index]))
- if err != nil {
- t.Fatal(err)
- }
-
- req := &testpb.StreamingOutputCallRequest{
- ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
- ResponseParameters: respParam,
- Payload: payload,
- }
- if err := stream.Send(req); err != nil {
- t.Fatalf("%v.Send(%v) = %v, want ", stream, req, err)
- }
- reply, err := stream.Recv()
- if err != nil {
- t.Fatalf("%v.Recv() = %v, want ", stream, err)
- }
- pt := reply.GetPayload().GetType()
- if pt != testpb.PayloadType_COMPRESSABLE {
- t.Fatalf("Got the reply of type %d, want %d", pt, testpb.PayloadType_COMPRESSABLE)
- }
- size := len(reply.GetPayload().GetBody())
- if size != int(respSizes[index]) {
- t.Fatalf("Got reply body of length %d, want %d", size, respSizes[index])
- }
- index++
- }
- if err := stream.CloseSend(); err != nil {
- t.Fatalf("%v.CloseSend() got %v, want %v", stream, err, nil)
- }
- if _, err := stream.Recv(); err != io.EOF {
- t.Fatalf("%v failed to complele the ping pong test: %v", stream, err)
- }
-}
-
-func TestMetadataStreamingRPC(t *testing.T) {
- for _, e := range listTestEnv() {
- testMetadataStreamingRPC(t, e)
- }
-}
-
-func testMetadataStreamingRPC(t *testing.T, e env) {
- s, addr := serverSetUp(t, nil, math.MaxUint32, nil, nil, e)
- cc := clientSetUp(t, addr, nil, nil, "", e)
- tc := testpb.NewTestServiceClient(cc)
- defer tearDown(s, cc)
- ctx := metadata.NewContext(context.Background(), testMetadata)
- stream, err := tc.FullDuplexCall(ctx)
- if err != nil {
- t.Fatalf("%v.FullDuplexCall(_) = _, %v, want ", tc, err)
- }
- go func() {
- headerMD, err := stream.Header()
- if e.security == "tls" {
- delete(headerMD, "transport_security_type")
- }
- if err != nil || !reflect.DeepEqual(testMetadata, headerMD) {
- t.Errorf("#1 %v.Header() = %v, %v, want %v, ", stream, headerMD, err, testMetadata)
- }
- // test the cached value.
- headerMD, err = stream.Header()
- if err != nil || !reflect.DeepEqual(testMetadata, headerMD) {
- t.Errorf("#2 %v.Header() = %v, %v, want %v, ", stream, headerMD, err, testMetadata)
- }
- var index int
- for index < len(reqSizes) {
- respParam := []*testpb.ResponseParameters{
- {
- Size: proto.Int32(int32(respSizes[index])),
- },
- }
-
- payload, err := newPayload(testpb.PayloadType_COMPRESSABLE, int32(reqSizes[index]))
- if err != nil {
- t.Fatal(err)
- }
-
- req := &testpb.StreamingOutputCallRequest{
- ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
- ResponseParameters: respParam,
- Payload: payload,
- }
- if err := stream.Send(req); err != nil {
- t.Errorf("%v.Send(%v) = %v, want ", stream, req, err)
- return
- }
- index++
- }
- // Tell the server we're done sending args.
- stream.CloseSend()
- }()
- for {
- if _, err := stream.Recv(); err != nil {
- break
- }
- }
- trailerMD := stream.Trailer()
- if !reflect.DeepEqual(testMetadata, trailerMD) {
- t.Fatalf("%v.Trailer() = %v, want %v", stream, trailerMD, testMetadata)
- }
-}
-
-func TestServerStreaming(t *testing.T) {
- for _, e := range listTestEnv() {
- testServerStreaming(t, e)
- }
-}
-
-func testServerStreaming(t *testing.T, e env) {
- s, addr := serverSetUp(t, nil, math.MaxUint32, nil, nil, e)
- cc := clientSetUp(t, addr, nil, nil, "", e)
- tc := testpb.NewTestServiceClient(cc)
- defer tearDown(s, cc)
- respParam := make([]*testpb.ResponseParameters, len(respSizes))
- for i, s := range respSizes {
- respParam[i] = &testpb.ResponseParameters{
- Size: proto.Int32(int32(s)),
- }
- }
- req := &testpb.StreamingOutputCallRequest{
- ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
- ResponseParameters: respParam,
- }
- stream, err := tc.StreamingOutputCall(context.Background(), req)
- if err != nil {
- t.Fatalf("%v.StreamingOutputCall(_) = _, %v, want ", tc, err)
- }
- var rpcStatus error
- var respCnt int
- var index int
- for {
- reply, err := stream.Recv()
- if err != nil {
- rpcStatus = err
- break
- }
- pt := reply.GetPayload().GetType()
- if pt != testpb.PayloadType_COMPRESSABLE {
- t.Fatalf("Got the reply of type %d, want %d", pt, testpb.PayloadType_COMPRESSABLE)
- }
- size := len(reply.GetPayload().GetBody())
- if size != int(respSizes[index]) {
- t.Fatalf("Got reply body of length %d, want %d", size, respSizes[index])
- }
- index++
- respCnt++
- }
- if rpcStatus != io.EOF {
- t.Fatalf("Failed to finish the server streaming rpc: %v, want ", rpcStatus)
- }
- if respCnt != len(respSizes) {
- t.Fatalf("Got %d reply, want %d", len(respSizes), respCnt)
- }
-}
-
-func TestFailedServerStreaming(t *testing.T) {
- for _, e := range listTestEnv() {
- testFailedServerStreaming(t, e)
- }
-}
-
-func testFailedServerStreaming(t *testing.T, e env) {
- s, addr := serverSetUp(t, nil, math.MaxUint32, nil, nil, e)
- cc := clientSetUp(t, addr, nil, nil, "", e)
- tc := testpb.NewTestServiceClient(cc)
- defer tearDown(s, cc)
- respParam := make([]*testpb.ResponseParameters, len(respSizes))
- for i, s := range respSizes {
- respParam[i] = &testpb.ResponseParameters{
- Size: proto.Int32(int32(s)),
- }
- }
- req := &testpb.StreamingOutputCallRequest{
- ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
- ResponseParameters: respParam,
- }
- ctx := metadata.NewContext(context.Background(), testMetadata)
- stream, err := tc.StreamingOutputCall(ctx, req)
- if err != nil {
- t.Fatalf("%v.StreamingOutputCall(_) = _, %v, want ", tc, err)
- }
- if _, err := stream.Recv(); err != grpc.Errorf(codes.DataLoss, "got extra metadata") {
- t.Fatalf("%v.Recv() = _, %v, want _, %v", stream, err, grpc.Errorf(codes.DataLoss, "got extra metadata"))
- }
-}
-
-func TestClientStreaming(t *testing.T) {
- for _, e := range listTestEnv() {
- testClientStreaming(t, e)
- }
-}
-
-func testClientStreaming(t *testing.T, e env) {
- s, addr := serverSetUp(t, nil, math.MaxUint32, nil, nil, e)
- cc := clientSetUp(t, addr, nil, nil, "", e)
- tc := testpb.NewTestServiceClient(cc)
- defer tearDown(s, cc)
- stream, err := tc.StreamingInputCall(context.Background())
- if err != nil {
- t.Fatalf("%v.StreamingInputCall(_) = _, %v, want ", tc, err)
- }
- var sum int
-
- for _, s := range reqSizes {
- payload, err := newPayload(testpb.PayloadType_COMPRESSABLE, int32(s))
- if err != nil {
- t.Fatal(err)
- }
-
- req := &testpb.StreamingInputCallRequest{
- Payload: payload,
- }
- if err := stream.Send(req); err != nil {
- t.Fatalf("%v.Send(%v) = %v, want ", stream, req, err)
- }
- sum += s
- }
- reply, err := stream.CloseAndRecv()
- if err != nil {
- t.Fatalf("%v.CloseAndRecv() got error %v, want %v", stream, err, nil)
- }
- if reply.GetAggregatedPayloadSize() != int32(sum) {
- t.Fatalf("%v.CloseAndRecv().GetAggregatePayloadSize() = %v; want %v", stream, reply.GetAggregatedPayloadSize(), sum)
- }
-}
-
-func TestExceedMaxStreamsLimit(t *testing.T) {
- for _, e := range listTestEnv() {
- testExceedMaxStreamsLimit(t, e)
- }
-}
-
-func testExceedMaxStreamsLimit(t *testing.T, e env) {
- // Only allows 1 live stream per server transport.
- s, addr := serverSetUp(t, nil, 1, nil, nil, e)
- cc := clientSetUp(t, addr, nil, nil, "", e)
- tc := testpb.NewTestServiceClient(cc)
- defer tearDown(s, cc)
- _, err := tc.StreamingInputCall(context.Background())
- if err != nil {
- t.Fatalf("%v.StreamingInputCall(_) = _, %v, want _, ", tc, err)
- }
- // Loop until receiving the new max stream setting from the server.
- for {
- ctx, _ := context.WithTimeout(context.Background(), time.Second)
- _, err := tc.StreamingInputCall(ctx)
- if err == nil {
- time.Sleep(time.Second)
- continue
- }
- if grpc.Code(err) == codes.DeadlineExceeded {
- break
- }
- t.Fatalf("%v.StreamingInputCall(_) = _, %v, want _, %d", tc, err, codes.DeadlineExceeded)
- }
-}
-
-func TestCompressServerHasNoSupport(t *testing.T) {
- for _, e := range listTestEnv() {
- testCompressServerHasNoSupport(t, e)
- }
-}
-
-func testCompressServerHasNoSupport(t *testing.T, e env) {
- s, addr := serverSetUp(t, nil, math.MaxUint32, nil, nil, e)
- cc := clientSetUp(t, addr, grpc.NewGZIPCompressor, nil, "", e)
- // Unary call
- tc := testpb.NewTestServiceClient(cc)
- defer tearDown(s, cc)
- argSize := 271828
- respSize := 314159
- payload, err := newPayload(testpb.PayloadType_COMPRESSABLE, int32(argSize))
- if err != nil {
- t.Fatal(err)
- }
- req := &testpb.SimpleRequest{
- ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
- ResponseSize: proto.Int32(int32(respSize)),
- Payload: payload,
- }
- if _, err := tc.UnaryCall(context.Background(), req); err == nil || grpc.Code(err) != codes.InvalidArgument {
- t.Fatalf("TestService/UnaryCall(_, _) = _, %v, want _, error code %d", err, codes.InvalidArgument)
- }
- // Streaming RPC
- stream, err := tc.FullDuplexCall(context.Background())
- if err != nil {
- t.Fatalf("%v.FullDuplexCall(_) = _, %v, want ", tc, err)
- }
- respParam := []*testpb.ResponseParameters{
- {
- Size: proto.Int32(31415),
- },
- }
- payload, err = newPayload(testpb.PayloadType_COMPRESSABLE, int32(31415))
- if err != nil {
- t.Fatal(err)
- }
- sreq := &testpb.StreamingOutputCallRequest{
- ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
- ResponseParameters: respParam,
- Payload: payload,
- }
- if err := stream.Send(sreq); err != nil {
- t.Fatalf("%v.Send(%v) = %v, want ", stream, sreq, err)
- }
- if _, err := stream.Recv(); err == nil || grpc.Code(err) != codes.InvalidArgument {
- t.Fatalf("%v.Recv() = %v, want error code %d", stream, err, codes.InvalidArgument)
- }
-}
-
-func TestCompressOK(t *testing.T) {
- for _, e := range listTestEnv() {
- testCompressOK(t, e)
- }
-}
-
-func testCompressOK(t *testing.T, e env) {
- s, addr := serverSetUp(t, nil, math.MaxUint32, grpc.NewGZIPCompressor, grpc.NewGZIPDecompressor, e)
- cc := clientSetUp(t, addr, grpc.NewGZIPCompressor, grpc.NewGZIPDecompressor, "", e)
- // Unary call
- tc := testpb.NewTestServiceClient(cc)
- defer tearDown(s, cc)
- argSize := 271828
- respSize := 314159
- payload, err := newPayload(testpb.PayloadType_COMPRESSABLE, int32(argSize))
- if err != nil {
- t.Fatal(err)
- }
- req := &testpb.SimpleRequest{
- ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
- ResponseSize: proto.Int32(int32(respSize)),
- Payload: payload,
- }
- if _, err := tc.UnaryCall(context.Background(), req); err != nil {
- t.Fatalf("TestService/UnaryCall(_, _) = _, %v, want _, ", err)
- }
- // Streaming RPC
- stream, err := tc.FullDuplexCall(context.Background())
- if err != nil {
- t.Fatalf("%v.FullDuplexCall(_) = _, %v, want ", tc, err)
- }
- respParam := []*testpb.ResponseParameters{
- {
- Size: proto.Int32(31415),
- },
- }
- payload, err = newPayload(testpb.PayloadType_COMPRESSABLE, int32(31415))
- if err != nil {
- t.Fatal(err)
- }
- sreq := &testpb.StreamingOutputCallRequest{
- ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
- ResponseParameters: respParam,
- Payload: payload,
- }
- if err := stream.Send(sreq); err != nil {
- t.Fatalf("%v.Send(%v) = %v, want ", stream, sreq, err)
- }
- if _, err := stream.Recv(); err != nil {
- t.Fatalf("%v.Recv() = %v, want ", stream, err)
- }
-}
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/test/grpc_testing/test.pb.go b/Godeps/_workspace/src/google.golang.org/grpc/test/grpc_testing/test.pb.go
index 158379eb8cc..af982088ddf 100644
--- a/Godeps/_workspace/src/google.golang.org/grpc/test/grpc_testing/test.pb.go
+++ b/Godeps/_workspace/src/google.golang.org/grpc/test/grpc_testing/test.pb.go
@@ -22,6 +22,7 @@ It has these top-level messages:
package grpc_testing
import proto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+import fmt "fmt"
import math "math"
import (
@@ -29,14 +30,15 @@ import (
grpc "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc"
)
-// Reference imports to suppress errors if they are not otherwise used.
-var _ context.Context
-var _ grpc.ClientConn
-
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
+var _ = fmt.Errorf
var _ = math.Inf
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+const _ = proto.ProtoPackageIsVersion1
+
// The type of payload that should be returned.
type PayloadType int32
@@ -76,14 +78,16 @@ func (x *PayloadType) UnmarshalJSON(data []byte) error {
*x = PayloadType(value)
return nil
}
+func (PayloadType) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
type Empty struct {
XXX_unrecognized []byte `json:"-"`
}
-func (m *Empty) Reset() { *m = Empty{} }
-func (m *Empty) String() string { return proto.CompactTextString(m) }
-func (*Empty) ProtoMessage() {}
+func (m *Empty) Reset() { *m = Empty{} }
+func (m *Empty) String() string { return proto.CompactTextString(m) }
+func (*Empty) ProtoMessage() {}
+func (*Empty) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
// A block of data, to simply increase gRPC message size.
type Payload struct {
@@ -94,9 +98,10 @@ type Payload struct {
XXX_unrecognized []byte `json:"-"`
}
-func (m *Payload) Reset() { *m = Payload{} }
-func (m *Payload) String() string { return proto.CompactTextString(m) }
-func (*Payload) ProtoMessage() {}
+func (m *Payload) Reset() { *m = Payload{} }
+func (m *Payload) String() string { return proto.CompactTextString(m) }
+func (*Payload) ProtoMessage() {}
+func (*Payload) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func (m *Payload) GetType() PayloadType {
if m != nil && m.Type != nil {
@@ -129,9 +134,10 @@ type SimpleRequest struct {
XXX_unrecognized []byte `json:"-"`
}
-func (m *SimpleRequest) Reset() { *m = SimpleRequest{} }
-func (m *SimpleRequest) String() string { return proto.CompactTextString(m) }
-func (*SimpleRequest) ProtoMessage() {}
+func (m *SimpleRequest) Reset() { *m = SimpleRequest{} }
+func (m *SimpleRequest) String() string { return proto.CompactTextString(m) }
+func (*SimpleRequest) ProtoMessage() {}
+func (*SimpleRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
func (m *SimpleRequest) GetResponseType() PayloadType {
if m != nil && m.ResponseType != nil {
@@ -180,9 +186,10 @@ type SimpleResponse struct {
XXX_unrecognized []byte `json:"-"`
}
-func (m *SimpleResponse) Reset() { *m = SimpleResponse{} }
-func (m *SimpleResponse) String() string { return proto.CompactTextString(m) }
-func (*SimpleResponse) ProtoMessage() {}
+func (m *SimpleResponse) Reset() { *m = SimpleResponse{} }
+func (m *SimpleResponse) String() string { return proto.CompactTextString(m) }
+func (*SimpleResponse) ProtoMessage() {}
+func (*SimpleResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
func (m *SimpleResponse) GetPayload() *Payload {
if m != nil {
@@ -212,9 +219,10 @@ type StreamingInputCallRequest struct {
XXX_unrecognized []byte `json:"-"`
}
-func (m *StreamingInputCallRequest) Reset() { *m = StreamingInputCallRequest{} }
-func (m *StreamingInputCallRequest) String() string { return proto.CompactTextString(m) }
-func (*StreamingInputCallRequest) ProtoMessage() {}
+func (m *StreamingInputCallRequest) Reset() { *m = StreamingInputCallRequest{} }
+func (m *StreamingInputCallRequest) String() string { return proto.CompactTextString(m) }
+func (*StreamingInputCallRequest) ProtoMessage() {}
+func (*StreamingInputCallRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
func (m *StreamingInputCallRequest) GetPayload() *Payload {
if m != nil {
@@ -230,9 +238,10 @@ type StreamingInputCallResponse struct {
XXX_unrecognized []byte `json:"-"`
}
-func (m *StreamingInputCallResponse) Reset() { *m = StreamingInputCallResponse{} }
-func (m *StreamingInputCallResponse) String() string { return proto.CompactTextString(m) }
-func (*StreamingInputCallResponse) ProtoMessage() {}
+func (m *StreamingInputCallResponse) Reset() { *m = StreamingInputCallResponse{} }
+func (m *StreamingInputCallResponse) String() string { return proto.CompactTextString(m) }
+func (*StreamingInputCallResponse) ProtoMessage() {}
+func (*StreamingInputCallResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
func (m *StreamingInputCallResponse) GetAggregatedPayloadSize() int32 {
if m != nil && m.AggregatedPayloadSize != nil {
@@ -252,9 +261,10 @@ type ResponseParameters struct {
XXX_unrecognized []byte `json:"-"`
}
-func (m *ResponseParameters) Reset() { *m = ResponseParameters{} }
-func (m *ResponseParameters) String() string { return proto.CompactTextString(m) }
-func (*ResponseParameters) ProtoMessage() {}
+func (m *ResponseParameters) Reset() { *m = ResponseParameters{} }
+func (m *ResponseParameters) String() string { return proto.CompactTextString(m) }
+func (*ResponseParameters) ProtoMessage() {}
+func (*ResponseParameters) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
func (m *ResponseParameters) GetSize() int32 {
if m != nil && m.Size != nil {
@@ -284,9 +294,10 @@ type StreamingOutputCallRequest struct {
XXX_unrecognized []byte `json:"-"`
}
-func (m *StreamingOutputCallRequest) Reset() { *m = StreamingOutputCallRequest{} }
-func (m *StreamingOutputCallRequest) String() string { return proto.CompactTextString(m) }
-func (*StreamingOutputCallRequest) ProtoMessage() {}
+func (m *StreamingOutputCallRequest) Reset() { *m = StreamingOutputCallRequest{} }
+func (m *StreamingOutputCallRequest) String() string { return proto.CompactTextString(m) }
+func (*StreamingOutputCallRequest) ProtoMessage() {}
+func (*StreamingOutputCallRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} }
func (m *StreamingOutputCallRequest) GetResponseType() PayloadType {
if m != nil && m.ResponseType != nil {
@@ -316,9 +327,10 @@ type StreamingOutputCallResponse struct {
XXX_unrecognized []byte `json:"-"`
}
-func (m *StreamingOutputCallResponse) Reset() { *m = StreamingOutputCallResponse{} }
-func (m *StreamingOutputCallResponse) String() string { return proto.CompactTextString(m) }
-func (*StreamingOutputCallResponse) ProtoMessage() {}
+func (m *StreamingOutputCallResponse) Reset() { *m = StreamingOutputCallResponse{} }
+func (m *StreamingOutputCallResponse) String() string { return proto.CompactTextString(m) }
+func (*StreamingOutputCallResponse) ProtoMessage() {}
+func (*StreamingOutputCallResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} }
func (m *StreamingOutputCallResponse) GetPayload() *Payload {
if m != nil {
@@ -328,9 +340,22 @@ func (m *StreamingOutputCallResponse) GetPayload() *Payload {
}
func init() {
+ proto.RegisterType((*Empty)(nil), "grpc.testing.Empty")
+ proto.RegisterType((*Payload)(nil), "grpc.testing.Payload")
+ proto.RegisterType((*SimpleRequest)(nil), "grpc.testing.SimpleRequest")
+ proto.RegisterType((*SimpleResponse)(nil), "grpc.testing.SimpleResponse")
+ proto.RegisterType((*StreamingInputCallRequest)(nil), "grpc.testing.StreamingInputCallRequest")
+ proto.RegisterType((*StreamingInputCallResponse)(nil), "grpc.testing.StreamingInputCallResponse")
+ proto.RegisterType((*ResponseParameters)(nil), "grpc.testing.ResponseParameters")
+ proto.RegisterType((*StreamingOutputCallRequest)(nil), "grpc.testing.StreamingOutputCallRequest")
+ proto.RegisterType((*StreamingOutputCallResponse)(nil), "grpc.testing.StreamingOutputCallResponse")
proto.RegisterEnum("grpc.testing.PayloadType", PayloadType_name, PayloadType_value)
}
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConn
+
// Client API for TestService service
type TestServiceClient interface {
@@ -700,3 +725,43 @@ var _TestService_serviceDesc = grpc.ServiceDesc{
},
},
}
+
+var fileDescriptor0 = []byte{
+ // 567 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xbc, 0x54, 0x51, 0x6f, 0xd2, 0x50,
+ 0x14, 0xb6, 0x03, 0x64, 0x1c, 0x58, 0x43, 0x0e, 0x59, 0x64, 0x9d, 0x89, 0x4b, 0x7d, 0xb0, 0x9a,
+ 0x88, 0x86, 0x44, 0x1f, 0x35, 0x73, 0x63, 0x71, 0x09, 0x03, 0x6c, 0xe1, 0x99, 0x5c, 0xe1, 0x0e,
+ 0x9b, 0x94, 0xb6, 0xb6, 0xb7, 0x46, 0x7c, 0xf0, 0x8f, 0xf9, 0x67, 0xfc, 0x11, 0xfe, 0x00, 0xef,
+ 0xbd, 0x6d, 0xa1, 0x40, 0x17, 0x99, 0xc6, 0xbd, 0xb5, 0xdf, 0xf9, 0xce, 0x77, 0xbe, 0xef, 0x9e,
+ 0xdb, 0x02, 0x30, 0x1a, 0xb2, 0x96, 0x1f, 0x78, 0xcc, 0xc3, 0xda, 0x2c, 0xf0, 0x27, 0x2d, 0x01,
+ 0xd8, 0xee, 0x4c, 0x2f, 0x43, 0xa9, 0x33, 0xf7, 0xd9, 0x42, 0xef, 0x42, 0x79, 0x40, 0x16, 0x8e,
+ 0x47, 0xa6, 0xf8, 0x1c, 0x8a, 0x6c, 0xe1, 0xd3, 0xa6, 0x72, 0xa2, 0x18, 0x6a, 0xfb, 0xa8, 0x95,
+ 0x6d, 0x68, 0x25, 0xa4, 0x21, 0x27, 0x98, 0x92, 0x86, 0x08, 0xc5, 0x8f, 0xde, 0x74, 0xd1, 0xdc,
+ 0xe3, 0xf4, 0x9a, 0x29, 0x9f, 0xf5, 0x5f, 0x0a, 0x1c, 0x58, 0xf6, 0xdc, 0x77, 0xa8, 0x49, 0x3f,
+ 0x47, 0xbc, 0x15, 0xdf, 0xc0, 0x41, 0x40, 0x43, 0xdf, 0x73, 0x43, 0x3a, 0xde, 0x4d, 0xbd, 0x96,
+ 0xf2, 0xc5, 0x1b, 0x3e, 0xce, 0xf4, 0x87, 0xf6, 0x37, 0x2a, 0xc7, 0x95, 0x56, 0x24, 0x8b, 0x63,
+ 0xf8, 0x02, 0xca, 0x7e, 0xac, 0xd0, 0x2c, 0xf0, 0x72, 0xb5, 0x7d, 0x98, 0x2b, 0x6f, 0xa6, 0x2c,
+ 0xa1, 0x7a, 0x6d, 0x3b, 0xce, 0x38, 0x0a, 0x69, 0xe0, 0x92, 0x39, 0x6d, 0x16, 0x79, 0xdb, 0xbe,
+ 0x59, 0x13, 0xe0, 0x28, 0xc1, 0xd0, 0x80, 0xba, 0x24, 0x79, 0x24, 0x62, 0x9f, 0xc6, 0xe1, 0xc4,
+ 0xe3, 0xee, 0x4b, 0x92, 0xa7, 0x0a, 0xbc, 0x2f, 0x60, 0x4b, 0xa0, 0xfa, 0x77, 0x50, 0xd3, 0xd4,
+ 0xb1, 0xab, 0xac, 0x23, 0x65, 0x27, 0x47, 0x1a, 0xec, 0x2f, 0xcd, 0x88, 0x88, 0x15, 0x73, 0xf9,
+ 0x8e, 0x8f, 0xa0, 0x9a, 0xf5, 0x50, 0x90, 0x65, 0xf0, 0x56, 0xf3, 0xbb, 0x70, 0x64, 0xb1, 0x80,
+ 0x92, 0x39, 0x97, 0xbe, 0x74, 0xfd, 0x88, 0x9d, 0x11, 0xc7, 0x49, 0x37, 0x70, 0x5b, 0x2b, 0xfa,
+ 0x10, 0xb4, 0x3c, 0xb5, 0x24, 0xd9, 0x6b, 0x78, 0x40, 0x66, 0xb3, 0x80, 0xce, 0x08, 0xa3, 0xd3,
+ 0x71, 0xd2, 0x13, 0xaf, 0x46, 0x91, 0xab, 0x39, 0x5c, 0x95, 0x13, 0x69, 0xb1, 0x23, 0xfd, 0x12,
+ 0x30, 0xd5, 0x18, 0x90, 0x80, 0xc7, 0x62, 0x34, 0x08, 0xc5, 0x25, 0xca, 0xb4, 0xca, 0x67, 0x11,
+ 0xd7, 0x76, 0x79, 0xf5, 0x0b, 0x11, 0x0b, 0x4a, 0x16, 0x0e, 0x29, 0x34, 0x0a, 0xf5, 0x9f, 0x4a,
+ 0xc6, 0x61, 0x3f, 0x62, 0x1b, 0x81, 0xff, 0xf5, 0xca, 0x7d, 0x80, 0xc6, 0xb2, 0xdf, 0x5f, 0x5a,
+ 0xe5, 0x3e, 0x0a, 0xfc, 0xf0, 0x4e, 0xd6, 0x55, 0xb6, 0x23, 0x99, 0x18, 0x6c, 0xc7, 0xbc, 0xed,
+ 0x05, 0xd5, 0x7b, 0x70, 0x9c, 0x9b, 0xf0, 0x2f, 0xaf, 0xd7, 0xb3, 0xb7, 0x50, 0xcd, 0x04, 0xc6,
+ 0x3a, 0xd4, 0xce, 0xfa, 0x57, 0x03, 0xb3, 0x63, 0x59, 0xa7, 0xef, 0xba, 0x9d, 0xfa, 0x3d, 0xbe,
+ 0x08, 0x75, 0xd4, 0x5b, 0xc3, 0x14, 0x04, 0xb8, 0x6f, 0x9e, 0xf6, 0xce, 0xfb, 0x57, 0xf5, 0xbd,
+ 0xf6, 0x8f, 0x22, 0x54, 0x87, 0x5c, 0xdd, 0xe2, 0x4b, 0xb0, 0x27, 0x14, 0x5f, 0x41, 0x45, 0xfe,
+ 0x40, 0x84, 0x2d, 0x6c, 0xac, 0x4f, 0x97, 0x05, 0x2d, 0x0f, 0xc4, 0x0b, 0xa8, 0x8c, 0x5c, 0x12,
+ 0xc4, 0x6d, 0xc7, 0xeb, 0x8c, 0xb5, 0x1f, 0x87, 0xf6, 0x30, 0xbf, 0x98, 0x1c, 0x80, 0x03, 0x8d,
+ 0x9c, 0xf3, 0x41, 0x63, 0xa3, 0xe9, 0xc6, 0x4b, 0xa2, 0x3d, 0xdd, 0x81, 0x19, 0xcf, 0x7a, 0xa9,
+ 0xa0, 0x0d, 0xb8, 0xfd, 0x45, 0xe0, 0x93, 0x1b, 0x24, 0x36, 0xbf, 0x40, 0xcd, 0xf8, 0x33, 0x31,
+ 0x1e, 0x65, 0x88, 0x51, 0xea, 0x45, 0xe4, 0x38, 0xe7, 0x11, 0x4f, 0xfb, 0xf5, 0xbf, 0x65, 0x32,
+ 0x14, 0x99, 0x4a, 0x7d, 0x4f, 0x9c, 0xeb, 0x3b, 0x18, 0xf5, 0x3b, 0x00, 0x00, 0xff, 0xff, 0x4c,
+ 0x41, 0xfe, 0xb6, 0x89, 0x06, 0x00, 0x00,
+}
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/test/testdata/ca.pem b/Godeps/_workspace/src/google.golang.org/grpc/test/testdata/ca.pem
deleted file mode 100644
index 6c8511a73c6..00000000000
--- a/Godeps/_workspace/src/google.golang.org/grpc/test/testdata/ca.pem
+++ /dev/null
@@ -1,15 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV
-BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
-aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla
-Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0
-YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT
-BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7
-+L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu
-g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd
-Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV
-HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau
-sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m
-oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG
-Dfcog5wrJytaQ6UA0wE=
------END CERTIFICATE-----
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/test/testdata/server1.key b/Godeps/_workspace/src/google.golang.org/grpc/test/testdata/server1.key
deleted file mode 100644
index 143a5b87658..00000000000
--- a/Godeps/_workspace/src/google.golang.org/grpc/test/testdata/server1.key
+++ /dev/null
@@ -1,16 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOHDFScoLCVJpYDD
-M4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1BgzkWF+slf
-3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd9N8YwbBY
-AckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAECgYAn7qGnM2vbjJNBm0VZCkOkTIWm
-V10okw7EPJrdL2mkre9NasghNXbE1y5zDshx5Nt3KsazKOxTT8d0Jwh/3KbaN+YY
-tTCbKGW0pXDRBhwUHRcuRzScjli8Rih5UOCiZkhefUTcRb6xIhZJuQy71tjaSy0p
-dHZRmYyBYO2YEQ8xoQJBAPrJPhMBkzmEYFtyIEqAxQ/o/A6E+E4w8i+KM7nQCK7q
-K4JXzyXVAjLfyBZWHGM2uro/fjqPggGD6QH1qXCkI4MCQQDmdKeb2TrKRh5BY1LR
-81aJGKcJ2XbcDu6wMZK4oqWbTX2KiYn9GB0woM6nSr/Y6iy1u145YzYxEV/iMwff
-DJULAkB8B2MnyzOg0pNFJqBJuH29bKCcHa8gHJzqXhNO5lAlEbMK95p/P2Wi+4Hd
-aiEIAF1BF326QJcvYKmwSmrORp85AkAlSNxRJ50OWrfMZnBgzVjDx3xG6KsFQVk2
-ol6VhqL6dFgKUORFUWBvnKSyhjJxurlPEahV6oo6+A+mPhFY8eUvAkAZQyTdupP3
-XEFQKctGz+9+gKkemDp7LBBMEMBXrGTLPhpEfcjv/7KPdnFHYmhYeBTBnuVmTVWe
-F98XJ7tIFfJq
------END PRIVATE KEY-----
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/test/testdata/server1.pem b/Godeps/_workspace/src/google.golang.org/grpc/test/testdata/server1.pem
deleted file mode 100644
index f3d43fcc5be..00000000000
--- a/Godeps/_workspace/src/google.golang.org/grpc/test/testdata/server1.pem
+++ /dev/null
@@ -1,16 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICnDCCAgWgAwIBAgIBBzANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJBVTET
-MBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ
-dHkgTHRkMQ8wDQYDVQQDEwZ0ZXN0Y2EwHhcNMTUxMTA0MDIyMDI0WhcNMjUxMTAx
-MDIyMDI0WjBlMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV
-BAcTB0NoaWNhZ28xFTATBgNVBAoTDEV4YW1wbGUsIENvLjEaMBgGA1UEAxQRKi50
-ZXN0Lmdvb2dsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOHDFSco
-LCVJpYDDM4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1Bg
-zkWF+slf3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd
-9N8YwbBYAckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAGjazBpMAkGA1UdEwQCMAAw
-CwYDVR0PBAQDAgXgME8GA1UdEQRIMEaCECoudGVzdC5nb29nbGUuZnKCGHdhdGVy
-em9vaS50ZXN0Lmdvb2dsZS5iZYISKi50ZXN0LnlvdXR1YmUuY29thwTAqAEDMA0G
-CSqGSIb3DQEBCwUAA4GBAJFXVifQNub1LUP4JlnX5lXNlo8FxZ2a12AFQs+bzoJ6
-hM044EDjqyxUqSbVePK0ni3w1fHQB5rY9yYC5f8G7aqqTY1QOhoUk8ZTSTRpnkTh
-y4jjdvTZeLDVBlueZUTDRmy2feY5aZIU18vFDK08dTG0A87pppuv1LNIR3loveU8
------END CERTIFICATE-----
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/testdata/ca.pem b/Godeps/_workspace/src/google.golang.org/grpc/testdata/ca.pem
deleted file mode 100644
index 6c8511a73c6..00000000000
--- a/Godeps/_workspace/src/google.golang.org/grpc/testdata/ca.pem
+++ /dev/null
@@ -1,15 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV
-BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
-aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla
-Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0
-YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT
-BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7
-+L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu
-g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd
-Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV
-HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau
-sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m
-oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG
-Dfcog5wrJytaQ6UA0wE=
------END CERTIFICATE-----
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/testdata/server1.key b/Godeps/_workspace/src/google.golang.org/grpc/testdata/server1.key
deleted file mode 100644
index 143a5b87658..00000000000
--- a/Godeps/_workspace/src/google.golang.org/grpc/testdata/server1.key
+++ /dev/null
@@ -1,16 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOHDFScoLCVJpYDD
-M4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1BgzkWF+slf
-3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd9N8YwbBY
-AckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAECgYAn7qGnM2vbjJNBm0VZCkOkTIWm
-V10okw7EPJrdL2mkre9NasghNXbE1y5zDshx5Nt3KsazKOxTT8d0Jwh/3KbaN+YY
-tTCbKGW0pXDRBhwUHRcuRzScjli8Rih5UOCiZkhefUTcRb6xIhZJuQy71tjaSy0p
-dHZRmYyBYO2YEQ8xoQJBAPrJPhMBkzmEYFtyIEqAxQ/o/A6E+E4w8i+KM7nQCK7q
-K4JXzyXVAjLfyBZWHGM2uro/fjqPggGD6QH1qXCkI4MCQQDmdKeb2TrKRh5BY1LR
-81aJGKcJ2XbcDu6wMZK4oqWbTX2KiYn9GB0woM6nSr/Y6iy1u145YzYxEV/iMwff
-DJULAkB8B2MnyzOg0pNFJqBJuH29bKCcHa8gHJzqXhNO5lAlEbMK95p/P2Wi+4Hd
-aiEIAF1BF326QJcvYKmwSmrORp85AkAlSNxRJ50OWrfMZnBgzVjDx3xG6KsFQVk2
-ol6VhqL6dFgKUORFUWBvnKSyhjJxurlPEahV6oo6+A+mPhFY8eUvAkAZQyTdupP3
-XEFQKctGz+9+gKkemDp7LBBMEMBXrGTLPhpEfcjv/7KPdnFHYmhYeBTBnuVmTVWe
-F98XJ7tIFfJq
------END PRIVATE KEY-----
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/testdata/server1.pem b/Godeps/_workspace/src/google.golang.org/grpc/testdata/server1.pem
deleted file mode 100644
index f3d43fcc5be..00000000000
--- a/Godeps/_workspace/src/google.golang.org/grpc/testdata/server1.pem
+++ /dev/null
@@ -1,16 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICnDCCAgWgAwIBAgIBBzANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJBVTET
-MBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ
-dHkgTHRkMQ8wDQYDVQQDEwZ0ZXN0Y2EwHhcNMTUxMTA0MDIyMDI0WhcNMjUxMTAx
-MDIyMDI0WjBlMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV
-BAcTB0NoaWNhZ28xFTATBgNVBAoTDEV4YW1wbGUsIENvLjEaMBgGA1UEAxQRKi50
-ZXN0Lmdvb2dsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOHDFSco
-LCVJpYDDM4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1Bg
-zkWF+slf3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd
-9N8YwbBYAckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAGjazBpMAkGA1UdEwQCMAAw
-CwYDVR0PBAQDAgXgME8GA1UdEQRIMEaCECoudGVzdC5nb29nbGUuZnKCGHdhdGVy
-em9vaS50ZXN0Lmdvb2dsZS5iZYISKi50ZXN0LnlvdXR1YmUuY29thwTAqAEDMA0G
-CSqGSIb3DQEBCwUAA4GBAJFXVifQNub1LUP4JlnX5lXNlo8FxZ2a12AFQs+bzoJ6
-hM044EDjqyxUqSbVePK0ni3w1fHQB5rY9yYC5f8G7aqqTY1QOhoUk8ZTSTRpnkTh
-y4jjdvTZeLDVBlueZUTDRmy2feY5aZIU18vFDK08dTG0A87pppuv1LNIR3loveU8
------END CERTIFICATE-----
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/transport/handler_server.go b/Godeps/_workspace/src/google.golang.org/grpc/transport/handler_server.go
new file mode 100644
index 00000000000..100ba876a97
--- /dev/null
+++ b/Godeps/_workspace/src/google.golang.org/grpc/transport/handler_server.go
@@ -0,0 +1,377 @@
+/*
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+// This file is the implementation of a gRPC server using HTTP/2 which
+// uses the standard Go http2 Server implementation (via the
+// http.Handler interface), rather than speaking low-level HTTP/2
+// frames itself. It is the implementation of *grpc.Server.ServeHTTP.
+
+package transport
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "net/http"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/http2"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codes"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/credentials"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/metadata"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/peer"
+)
+
+// NewServerHandlerTransport returns a ServerTransport handling gRPC
+// from inside an http.Handler. It requires that the http Server
+// supports HTTP/2.
+func NewServerHandlerTransport(w http.ResponseWriter, r *http.Request) (ServerTransport, error) {
+ if r.ProtoMajor != 2 {
+ return nil, errors.New("gRPC requires HTTP/2")
+ }
+ if r.Method != "POST" {
+ return nil, errors.New("invalid gRPC request method")
+ }
+ if !strings.Contains(r.Header.Get("Content-Type"), "application/grpc") {
+ return nil, errors.New("invalid gRPC request content-type")
+ }
+ if _, ok := w.(http.Flusher); !ok {
+ return nil, errors.New("gRPC requires a ResponseWriter supporting http.Flusher")
+ }
+ if _, ok := w.(http.CloseNotifier); !ok {
+ return nil, errors.New("gRPC requires a ResponseWriter supporting http.CloseNotifier")
+ }
+
+ st := &serverHandlerTransport{
+ rw: w,
+ req: r,
+ closedCh: make(chan struct{}),
+ writes: make(chan func()),
+ }
+
+ if v := r.Header.Get("grpc-timeout"); v != "" {
+ to, err := timeoutDecode(v)
+ if err != nil {
+ return nil, StreamErrorf(codes.Internal, "malformed time-out: %v", err)
+ }
+ st.timeoutSet = true
+ st.timeout = to
+ }
+
+ var metakv []string
+ for k, vv := range r.Header {
+ k = strings.ToLower(k)
+ if isReservedHeader(k) {
+ continue
+ }
+ for _, v := range vv {
+ if k == "user-agent" {
+ // user-agent is special. Copying logic of http_util.go.
+ if i := strings.LastIndex(v, " "); i == -1 {
+ // There is no application user agent string being set
+ continue
+ } else {
+ v = v[:i]
+ }
+ }
+ metakv = append(metakv, k, v)
+
+ }
+ }
+ st.headerMD = metadata.Pairs(metakv...)
+
+ return st, nil
+}
+
+// serverHandlerTransport is an implementation of ServerTransport
+// which replies to exactly one gRPC request (exactly one HTTP request),
+// using the net/http.Handler interface. This http.Handler is guranteed
+// at this point to be speaking over HTTP/2, so it's able to speak valid
+// gRPC.
+type serverHandlerTransport struct {
+ rw http.ResponseWriter
+ req *http.Request
+ timeoutSet bool
+ timeout time.Duration
+ didCommonHeaders bool
+
+ headerMD metadata.MD
+
+ closeOnce sync.Once
+ closedCh chan struct{} // closed on Close
+
+ // writes is a channel of code to run serialized in the
+ // ServeHTTP (HandleStreams) goroutine. The channel is closed
+ // when WriteStatus is called.
+ writes chan func()
+}
+
+func (ht *serverHandlerTransport) Close() error {
+ ht.closeOnce.Do(ht.closeCloseChanOnce)
+ return nil
+}
+
+func (ht *serverHandlerTransport) closeCloseChanOnce() { close(ht.closedCh) }
+
+func (ht *serverHandlerTransport) RemoteAddr() net.Addr { return strAddr(ht.req.RemoteAddr) }
+
+// strAddr is a net.Addr backed by either a TCP "ip:port" string, or
+// the empty string if unknown.
+type strAddr string
+
+func (a strAddr) Network() string {
+ if a != "" {
+ // Per the documentation on net/http.Request.RemoteAddr, if this is
+ // set, it's set to the IP:port of the peer (hence, TCP):
+ // https://golang.org/pkg/net/http/#Request
+ //
+ // If we want to support Unix sockets later, we can
+ // add our own grpc-specific convention within the
+ // grpc codebase to set RemoteAddr to a different
+ // format, or probably better: we can attach it to the
+ // context and use that from serverHandlerTransport.RemoteAddr.
+ return "tcp"
+ }
+ return ""
+}
+
+func (a strAddr) String() string { return string(a) }
+
+// do runs fn in the ServeHTTP goroutine.
+func (ht *serverHandlerTransport) do(fn func()) error {
+ select {
+ case ht.writes <- fn:
+ return nil
+ case <-ht.closedCh:
+ return ErrConnClosing
+ }
+}
+
+func (ht *serverHandlerTransport) WriteStatus(s *Stream, statusCode codes.Code, statusDesc string) error {
+ err := ht.do(func() {
+ ht.writeCommonHeaders(s)
+
+ // And flush, in case no header or body has been sent yet.
+ // This forces a separation of headers and trailers if this is the
+ // first call (for example, in end2end tests's TestNoService).
+ ht.rw.(http.Flusher).Flush()
+
+ h := ht.rw.Header()
+ h.Set("Grpc-Status", fmt.Sprintf("%d", statusCode))
+ if statusDesc != "" {
+ h.Set("Grpc-Message", statusDesc)
+ }
+ if md := s.Trailer(); len(md) > 0 {
+ for k, vv := range md {
+ for _, v := range vv {
+ // http2 ResponseWriter mechanism to
+ // send undeclared Trailers after the
+ // headers have possibly been written.
+ h.Add(http2.TrailerPrefix+k, v)
+ }
+ }
+ }
+ })
+ close(ht.writes)
+ return err
+}
+
+// writeCommonHeaders sets common headers on the first write
+// call (Write, WriteHeader, or WriteStatus).
+func (ht *serverHandlerTransport) writeCommonHeaders(s *Stream) {
+ if ht.didCommonHeaders {
+ return
+ }
+ ht.didCommonHeaders = true
+
+ h := ht.rw.Header()
+ h["Date"] = nil // suppress Date to make tests happy; TODO: restore
+ h.Set("Content-Type", "application/grpc")
+
+ // Predeclare trailers we'll set later in WriteStatus (after the body).
+ // This is a SHOULD in the HTTP RFC, and the way you add (known)
+ // Trailers per the net/http.ResponseWriter contract.
+ // See https://golang.org/pkg/net/http/#ResponseWriter
+ // and https://golang.org/pkg/net/http/#example_ResponseWriter_trailers
+ h.Add("Trailer", "Grpc-Status")
+ h.Add("Trailer", "Grpc-Message")
+
+ if s.sendCompress != "" {
+ h.Set("Grpc-Encoding", s.sendCompress)
+ }
+}
+
+func (ht *serverHandlerTransport) Write(s *Stream, data []byte, opts *Options) error {
+ return ht.do(func() {
+ ht.writeCommonHeaders(s)
+ ht.rw.Write(data)
+ if !opts.Delay {
+ ht.rw.(http.Flusher).Flush()
+ }
+ })
+}
+
+func (ht *serverHandlerTransport) WriteHeader(s *Stream, md metadata.MD) error {
+ return ht.do(func() {
+ ht.writeCommonHeaders(s)
+ h := ht.rw.Header()
+ for k, vv := range md {
+ for _, v := range vv {
+ h.Add(k, v)
+ }
+ }
+ ht.rw.WriteHeader(200)
+ ht.rw.(http.Flusher).Flush()
+ })
+}
+
+func (ht *serverHandlerTransport) HandleStreams(startStream func(*Stream)) {
+ // With this transport type there will be exactly 1 stream: this HTTP request.
+
+ var ctx context.Context
+ var cancel context.CancelFunc
+ if ht.timeoutSet {
+ ctx, cancel = context.WithTimeout(context.Background(), ht.timeout)
+ } else {
+ ctx, cancel = context.WithCancel(context.Background())
+ }
+
+ // requestOver is closed when either the request's context is done
+ // or the status has been written via WriteStatus.
+ requestOver := make(chan struct{})
+
+ // clientGone receives a single value if peer is gone, either
+ // because the underlying connection is dead or because the
+ // peer sends an http2 RST_STREAM.
+ clientGone := ht.rw.(http.CloseNotifier).CloseNotify()
+ go func() {
+ select {
+ case <-requestOver:
+ return
+ case <-ht.closedCh:
+ case <-clientGone:
+ }
+ cancel()
+ }()
+
+ req := ht.req
+
+ s := &Stream{
+ id: 0, // irrelevant
+ windowHandler: func(int) {}, // nothing
+ cancel: cancel,
+ buf: newRecvBuffer(),
+ st: ht,
+ method: req.URL.Path,
+ recvCompress: req.Header.Get("grpc-encoding"),
+ }
+ pr := &peer.Peer{
+ Addr: ht.RemoteAddr(),
+ }
+ if req.TLS != nil {
+ pr.AuthInfo = credentials.TLSInfo{*req.TLS}
+ }
+ ctx = metadata.NewContext(ctx, ht.headerMD)
+ ctx = peer.NewContext(ctx, pr)
+ s.ctx = newContextWithStream(ctx, s)
+ s.dec = &recvBufferReader{ctx: s.ctx, recv: s.buf}
+
+ // readerDone is closed when the Body.Read-ing goroutine exits.
+ readerDone := make(chan struct{})
+ go func() {
+ defer close(readerDone)
+ for {
+ buf := make([]byte, 1024) // TODO: minimize garbage, optimize recvBuffer code/ownership
+ n, err := req.Body.Read(buf)
+ if n > 0 {
+ s.buf.put(&recvMsg{data: buf[:n]})
+ }
+ if err != nil {
+ s.buf.put(&recvMsg{err: mapRecvMsgError(err)})
+ return
+ }
+ }
+ }()
+
+ // startStream is provided by the *grpc.Server's serveStreams.
+ // It starts a goroutine serving s and exits immediately.
+ // The goroutine that is started is the one that then calls
+ // into ht, calling WriteHeader, Write, WriteStatus, Close, etc.
+ startStream(s)
+
+ ht.runStream()
+ close(requestOver)
+
+ // Wait for reading goroutine to finish.
+ req.Body.Close()
+ <-readerDone
+}
+
+func (ht *serverHandlerTransport) runStream() {
+ for {
+ select {
+ case fn, ok := <-ht.writes:
+ if !ok {
+ return
+ }
+ fn()
+ case <-ht.closedCh:
+ return
+ }
+ }
+}
+
+// mapRecvMsgError returns the non-nil err into the appropriate
+// error value as expected by callers of *grpc.parser.recvMsg.
+// In particular, in can only be:
+// * io.EOF
+// * io.ErrUnexpectedEOF
+// * of type transport.ConnectionError
+// * of type transport.StreamError
+func mapRecvMsgError(err error) error {
+ if err == io.EOF || err == io.ErrUnexpectedEOF {
+ return err
+ }
+ if se, ok := err.(http2.StreamError); ok {
+ if code, ok := http2ErrConvTab[se.Code]; ok {
+ return StreamError{
+ Code: code,
+ Desc: se.Error(),
+ }
+ }
+ }
+ return ConnectionError{Desc: err.Error()}
+}
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/transport/http2_client.go b/Godeps/_workspace/src/google.golang.org/grpc/transport/http2_client.go
index c93de85ad15..c3992dc3a7b 100644
--- a/Godeps/_workspace/src/google.golang.org/grpc/transport/http2_client.go
+++ b/Godeps/_workspace/src/google.golang.org/grpc/transport/http2_client.go
@@ -353,6 +353,10 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea
} else {
endHeaders = true
}
+ var flush bool
+ if endHeaders && (hasMD || callHdr.Flush) {
+ flush = true
+ }
if first {
// Sends a HeadersFrame to server to start a new stream.
p := http2.HeadersFrameParam{
@@ -364,11 +368,11 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea
// Do a force flush for the buffered frames iff it is the last headers frame
// and there is header metadata to be sent. Otherwise, there is flushing until
// the corresponding data frame is written.
- err = t.framer.writeHeaders(hasMD && endHeaders, p)
+ err = t.framer.writeHeaders(flush, p)
first = false
} else {
// Sends Continuation frames for the leftover headers.
- err = t.framer.writeContinuation(hasMD && endHeaders, s.id, endHeaders, t.hBuf.Next(size))
+ err = t.framer.writeContinuation(flush, s.id, endHeaders, t.hBuf.Next(size))
}
if err != nil {
t.notifyError(err)
@@ -546,14 +550,8 @@ func (t *http2Client) Write(s *Stream, data []byte, opts *Options) error {
func (t *http2Client) getStream(f http2.Frame) (*Stream, bool) {
t.mu.Lock()
defer t.mu.Unlock()
- if t.activeStreams == nil {
- // The transport is closing.
- return nil, false
- }
- if s, ok := t.activeStreams[f.Header().StreamID]; ok {
- return s, true
- }
- return nil, false
+ s, ok := t.activeStreams[f.Header().StreamID]
+ return s, ok
}
// updateWindow adjusts the inbound quota for the stream and the transport.
@@ -633,7 +631,7 @@ func (t *http2Client) handleRSTStream(f *http2.RSTStreamFrame) {
close(s.headerChan)
s.headerDone = true
}
- s.statusCode, ok = http2RSTErrConvTab[http2.ErrCode(f.ErrCode)]
+ s.statusCode, ok = http2ErrConvTab[http2.ErrCode(f.ErrCode)]
if !ok {
grpclog.Println("transport: http2Client.handleRSTStream found no mapped gRPC status for the received http2 error ", f.ErrCode)
}
@@ -676,54 +674,49 @@ func (t *http2Client) handleWindowUpdate(f *http2.WindowUpdateFrame) {
}
}
-// operateHeader takes action on the decoded headers. It returns the current
-// stream if there are remaining headers on the wire (in the following
-// Continuation frame).
-func (t *http2Client) operateHeaders(hDec *hpackDecoder, s *Stream, frame headerFrame, endStream bool) (pendingStream *Stream) {
- defer func() {
- if pendingStream == nil {
- hDec.state = decodeState{}
- }
- }()
- endHeaders, err := hDec.decodeClientHTTP2Headers(frame)
- if s == nil {
- // s has been closed.
- return nil
+// operateHeaders takes action on the decoded headers.
+func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
+ s, ok := t.getStream(frame)
+ if !ok {
+ return
}
- if err != nil {
- s.write(recvMsg{err: err})
- // Something wrong. Stops reading even when there is remaining.
- return nil
+ var state decodeState
+ for _, hf := range frame.Fields {
+ state.processHeaderField(hf)
}
- if !endHeaders {
- return s
+ if state.err != nil {
+ s.write(recvMsg{err: state.err})
+ // Something wrong. Stops reading even when there is remaining.
+ return
}
+
+ endStream := frame.StreamEnded()
+
s.mu.Lock()
if !endStream {
- s.recvCompress = hDec.state.encoding
+ s.recvCompress = state.encoding
}
if !s.headerDone {
- if !endStream && len(hDec.state.mdata) > 0 {
- s.header = hDec.state.mdata
+ if !endStream && len(state.mdata) > 0 {
+ s.header = state.mdata
}
close(s.headerChan)
s.headerDone = true
}
if !endStream || s.state == streamDone {
s.mu.Unlock()
- return nil
+ return
}
- if len(hDec.state.mdata) > 0 {
- s.trailer = hDec.state.mdata
+ if len(state.mdata) > 0 {
+ s.trailer = state.mdata
}
s.state = streamDone
- s.statusCode = hDec.state.statusCode
- s.statusDesc = hDec.state.statusDesc
+ s.statusCode = state.statusCode
+ s.statusDesc = state.statusDesc
s.mu.Unlock()
s.write(recvMsg{err: io.EOF})
- return nil
}
// reader runs as a separate goroutine in charge of reading data from network
@@ -746,8 +739,6 @@ func (t *http2Client) reader() {
}
t.handleSettings(sf)
- hDec := newHPACKDecoder()
- var curStream *Stream
// loop to keep reading incoming messages on this transport.
for {
frame, err := t.framer.readFrame()
@@ -756,15 +747,8 @@ func (t *http2Client) reader() {
return
}
switch frame := frame.(type) {
- case *http2.HeadersFrame:
- // operateHeaders has to be invoked regardless the value of curStream
- // because the HPACK decoder needs to be updated using the received
- // headers.
- curStream, _ = t.getStream(frame)
- endStream := frame.Header().Flags.Has(http2.FlagHeadersEndStream)
- curStream = t.operateHeaders(hDec, curStream, frame, endStream)
- case *http2.ContinuationFrame:
- curStream = t.operateHeaders(hDec, curStream, frame, frame.HeadersEnded())
+ case *http2.MetaHeadersFrame:
+ t.operateHeaders(frame)
case *http2.DataFrame:
t.handleData(frame)
case *http2.RSTStreamFrame:
@@ -862,6 +846,17 @@ func (t *http2Client) Error() <-chan struct{} {
func (t *http2Client) notifyError(err error) {
t.mu.Lock()
defer t.mu.Unlock()
+
+ // Abort an active stream if the http2.Framer returns a
+ // http2.StreamError. This can happen only if the server's response
+ // is malformed http2.
+ if se, ok := err.(http2.StreamError); ok {
+ if s, ok := t.activeStreams[se.StreamID]; ok {
+ s.write(recvMsg{err: StreamErrorf(http2ErrConvTab[se.Code], "%v", err)})
+ return
+ }
+ }
+
// make sure t.errorChan is closed only once.
if t.state == reachable {
t.state = unreachable
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/transport/http2_server.go b/Godeps/_workspace/src/google.golang.org/grpc/transport/http2_server.go
index 49a78edc812..e68a9b0779f 100644
--- a/Godeps/_workspace/src/google.golang.org/grpc/transport/http2_server.go
+++ b/Godeps/_workspace/src/google.golang.org/grpc/transport/http2_server.go
@@ -62,8 +62,8 @@ type http2Server struct {
maxStreamID uint32 // max stream ID ever seen
authInfo credentials.AuthInfo // auth info about the connection
// writableChan synchronizes write access to the transport.
- // A writer acquires the write lock by sending a value on writableChan
- // and releases it by receiving from writableChan.
+ // A writer acquires the write lock by receiving a value on writableChan
+ // and releases it by sending on writableChan.
writableChan chan int
// shutdownChan is closed when Close is called.
// Blocking operations should select on shutdownChan to avoid
@@ -136,37 +136,38 @@ func newHTTP2Server(conn net.Conn, maxStreams uint32, authInfo credentials.AuthI
return t, nil
}
-// operateHeader takes action on the decoded headers. It returns the current
-// stream if there are remaining headers on the wire (in the following
-// Continuation frame).
-func (t *http2Server) operateHeaders(hDec *hpackDecoder, s *Stream, frame headerFrame, endStream bool, handle func(*Stream)) (pendingStream *Stream) {
- defer func() {
- if pendingStream == nil {
- hDec.state = decodeState{}
- }
- }()
- endHeaders, err := hDec.decodeServerHTTP2Headers(frame)
- if s == nil {
- // s has been closed.
- return nil
+// operateHeader takes action on the decoded headers.
+func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(*Stream)) {
+ buf := newRecvBuffer()
+ fc := &inFlow{
+ limit: initialWindowSize,
+ conn: t.fc,
+ }
+ s := &Stream{
+ id: frame.Header().StreamID,
+ st: t,
+ buf: buf,
+ fc: fc,
}
- if err != nil {
- grpclog.Printf("transport: http2Server.operateHeader found %v", err)
+
+ var state decodeState
+ for _, hf := range frame.Fields {
+ state.processHeaderField(hf)
+ }
+ if err := state.err; err != nil {
if se, ok := err.(StreamError); ok {
t.controlBuf.put(&resetStream{s.id, statusCodeConvTab[se.Code]})
}
- return nil
+ return
}
- if endStream {
+
+ if frame.StreamEnded() {
// s is just created by the caller. No lock needed.
s.state = streamReadDone
}
- if !endHeaders {
- return s
- }
- s.recvCompress = hDec.state.encoding
- if hDec.state.timeoutSet {
- s.ctx, s.cancel = context.WithTimeout(context.TODO(), hDec.state.timeout)
+ s.recvCompress = state.encoding
+ if state.timeoutSet {
+ s.ctx, s.cancel = context.WithTimeout(context.TODO(), state.timeout)
} else {
s.ctx, s.cancel = context.WithCancel(context.TODO())
}
@@ -183,25 +184,25 @@ func (t *http2Server) operateHeaders(hDec *hpackDecoder, s *Stream, frame header
// back to the client (unary call only).
s.ctx = newContextWithStream(s.ctx, s)
// Attach the received metadata to the context.
- if len(hDec.state.mdata) > 0 {
- s.ctx = metadata.NewContext(s.ctx, hDec.state.mdata)
+ if len(state.mdata) > 0 {
+ s.ctx = metadata.NewContext(s.ctx, state.mdata)
}
s.dec = &recvBufferReader{
ctx: s.ctx,
recv: s.buf,
}
- s.recvCompress = hDec.state.encoding
- s.method = hDec.state.method
+ s.recvCompress = state.encoding
+ s.method = state.method
t.mu.Lock()
if t.state != reachable {
t.mu.Unlock()
- return nil
+ return
}
if uint32(len(t.activeStreams)) >= t.maxStreams {
t.mu.Unlock()
t.controlBuf.put(&resetStream{s.id, http2.ErrCodeRefusedStream})
- return nil
+ return
}
s.sendQuotaPool = newQuotaPool(int(t.streamSendQuota))
t.activeStreams[s.id] = s
@@ -210,7 +211,6 @@ func (t *http2Server) operateHeaders(hDec *hpackDecoder, s *Stream, frame header
t.updateWindow(s, uint32(n))
}
handle(s)
- return nil
}
// HandleStreams receives incoming streams using the given handler. This is
@@ -243,8 +243,6 @@ func (t *http2Server) HandleStreams(handle func(*Stream)) {
}
t.handleSettings(sf)
- hDec := newHPACKDecoder()
- var curStream *Stream
for {
frame, err := t.framer.readFrame()
if err != nil {
@@ -252,7 +250,7 @@ func (t *http2Server) HandleStreams(handle func(*Stream)) {
return
}
switch frame := frame.(type) {
- case *http2.HeadersFrame:
+ case *http2.MetaHeadersFrame:
id := frame.Header().StreamID
if id%2 != 1 || id <= t.maxStreamID {
// illegal gRPC stream id.
@@ -261,21 +259,7 @@ func (t *http2Server) HandleStreams(handle func(*Stream)) {
break
}
t.maxStreamID = id
- buf := newRecvBuffer()
- fc := &inFlow{
- limit: initialWindowSize,
- conn: t.fc,
- }
- curStream = &Stream{
- id: frame.Header().StreamID,
- st: t,
- buf: buf,
- fc: fc,
- }
- endStream := frame.Header().Flags.Has(http2.FlagHeadersEndStream)
- curStream = t.operateHeaders(hDec, curStream, frame, endStream, handle)
- case *http2.ContinuationFrame:
- curStream = t.operateHeaders(hDec, curStream, frame, frame.HeadersEnded(), handle)
+ t.operateHeaders(frame, handle)
case *http2.DataFrame:
t.handleData(frame)
case *http2.RSTStreamFrame:
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/transport/http_util.go b/Godeps/_workspace/src/google.golang.org/grpc/transport/http_util.go
index 7b40284ad6f..b92cedc5144 100644
--- a/Godeps/_workspace/src/google.golang.org/grpc/transport/http_util.go
+++ b/Godeps/_workspace/src/google.golang.org/grpc/transport/http_util.go
@@ -62,8 +62,8 @@ const (
)
var (
- clientPreface = []byte(http2.ClientPreface)
- http2RSTErrConvTab = map[http2.ErrCode]codes.Code{
+ clientPreface = []byte(http2.ClientPreface)
+ http2ErrConvTab = map[http2.ErrCode]codes.Code{
http2.ErrCodeNo: codes.Internal,
http2.ErrCodeProtocol: codes.Internal,
http2.ErrCodeInternal: codes.Internal,
@@ -76,6 +76,7 @@ var (
http2.ErrCodeConnect: codes.Internal,
http2.ErrCodeEnhanceYourCalm: codes.ResourceExhausted,
http2.ErrCodeInadequateSecurity: codes.PermissionDenied,
+ http2.ErrCodeHTTP11Required: codes.FailedPrecondition,
}
statusCodeConvTab = map[codes.Code]http2.ErrCode{
codes.Internal: http2.ErrCodeInternal,
@@ -89,6 +90,8 @@ var (
// Records the states during HPACK decoding. Must be reset once the
// decoding of the entire headers are finished.
type decodeState struct {
+ err error // first error encountered decoding
+
encoding string
// statusCode caches the stream status received from the trailer
// the server sent. Client side only.
@@ -102,25 +105,11 @@ type decodeState struct {
mdata map[string][]string
}
-// An hpackDecoder decodes HTTP2 headers which may span multiple frames.
-type hpackDecoder struct {
- h *hpack.Decoder
- state decodeState
- err error // The err when decoding
-}
-
-// A headerFrame is either a http2.HeaderFrame or http2.ContinuationFrame.
-type headerFrame interface {
- Header() http2.FrameHeader
- HeaderBlockFragment() []byte
- HeadersEnded() bool
-}
-
// isReservedHeader checks whether hdr belongs to HTTP2 headers
// reserved by gRPC protocol. Any other headers are classified as the
// user-specified metadata.
func isReservedHeader(hdr string) bool {
- if hdr[0] == ':' {
+ if hdr != "" && hdr[0] == ':' {
return true
}
switch hdr {
@@ -137,100 +126,62 @@ func isReservedHeader(hdr string) bool {
}
}
-func newHPACKDecoder() *hpackDecoder {
- d := &hpackDecoder{}
- d.h = hpack.NewDecoder(http2InitHeaderTableSize, func(f hpack.HeaderField) {
- switch f.Name {
- case "content-type":
- if !strings.Contains(f.Value, "application/grpc") {
- d.err = StreamErrorf(codes.FailedPrecondition, "transport: received the unexpected header")
- return
+func (d *decodeState) setErr(err error) {
+ if d.err == nil {
+ d.err = err
+ }
+}
+
+func (d *decodeState) processHeaderField(f hpack.HeaderField) {
+ switch f.Name {
+ case "content-type":
+ if !strings.Contains(f.Value, "application/grpc") {
+ d.setErr(StreamErrorf(codes.FailedPrecondition, "transport: received the unexpected content-type %q", f.Value))
+ return
+ }
+ case "grpc-encoding":
+ d.encoding = f.Value
+ case "grpc-status":
+ code, err := strconv.Atoi(f.Value)
+ if err != nil {
+ d.setErr(StreamErrorf(codes.Internal, "transport: malformed grpc-status: %v", err))
+ return
+ }
+ d.statusCode = codes.Code(code)
+ case "grpc-message":
+ d.statusDesc = f.Value
+ case "grpc-timeout":
+ d.timeoutSet = true
+ var err error
+ d.timeout, err = timeoutDecode(f.Value)
+ if err != nil {
+ d.setErr(StreamErrorf(codes.Internal, "transport: malformed time-out: %v", err))
+ return
+ }
+ case ":path":
+ d.method = f.Value
+ default:
+ if !isReservedHeader(f.Name) {
+ if f.Name == "user-agent" {
+ i := strings.LastIndex(f.Value, " ")
+ if i == -1 {
+ // There is no application user agent string being set.
+ return
+ }
+ // Extract the application user agent string.
+ f.Value = f.Value[:i]
}
- case "grpc-encoding":
- d.state.encoding = f.Value
- case "grpc-status":
- code, err := strconv.Atoi(f.Value)
- if err != nil {
- d.err = StreamErrorf(codes.Internal, "transport: malformed grpc-status: %v", err)
- return
+ if d.mdata == nil {
+ d.mdata = make(map[string][]string)
}
- d.state.statusCode = codes.Code(code)
- case "grpc-message":
- d.state.statusDesc = f.Value
- case "grpc-timeout":
- d.state.timeoutSet = true
- var err error
- d.state.timeout, err = timeoutDecode(f.Value)
+ k, v, err := metadata.DecodeKeyValue(f.Name, f.Value)
if err != nil {
- d.err = StreamErrorf(codes.Internal, "transport: malformed time-out: %v", err)
+ grpclog.Printf("Failed to decode (%q, %q): %v", f.Name, f.Value, err)
return
}
- case ":path":
- d.state.method = f.Value
- default:
- if !isReservedHeader(f.Name) {
- if f.Name == "user-agent" {
- i := strings.LastIndex(f.Value, " ")
- if i == -1 {
- // There is no application user agent string being set.
- return
- }
- // Extract the application user agent string.
- f.Value = f.Value[:i]
- }
- if d.state.mdata == nil {
- d.state.mdata = make(map[string][]string)
- }
- k, v, err := metadata.DecodeKeyValue(f.Name, f.Value)
- if err != nil {
- grpclog.Printf("Failed to decode (%q, %q): %v", f.Name, f.Value, err)
- return
- }
- d.state.mdata[k] = append(d.state.mdata[k], v)
- }
- }
- })
- return d
-}
-
-func (d *hpackDecoder) decodeClientHTTP2Headers(frame headerFrame) (endHeaders bool, err error) {
- d.err = nil
- _, err = d.h.Write(frame.HeaderBlockFragment())
- if err != nil {
- err = StreamErrorf(codes.Internal, "transport: HPACK header decode error: %v", err)
- }
-
- if frame.HeadersEnded() {
- if closeErr := d.h.Close(); closeErr != nil && err == nil {
- err = StreamErrorf(codes.Internal, "transport: HPACK decoder close error: %v", closeErr)
- }
- endHeaders = true
- }
-
- if err == nil && d.err != nil {
- err = d.err
- }
- return
-}
-
-func (d *hpackDecoder) decodeServerHTTP2Headers(frame headerFrame) (endHeaders bool, err error) {
- d.err = nil
- _, err = d.h.Write(frame.HeaderBlockFragment())
- if err != nil {
- err = StreamErrorf(codes.Internal, "transport: HPACK header decode error: %v", err)
- }
-
- if frame.HeadersEnded() {
- if closeErr := d.h.Close(); closeErr != nil && err == nil {
- err = StreamErrorf(codes.Internal, "transport: HPACK decoder close error: %v", closeErr)
+ d.mdata[k] = append(d.mdata[k], v)
}
- endHeaders = true
}
-
- if err == nil && d.err != nil {
- err = d.err
- }
- return
}
type timeoutUnit uint8
@@ -325,6 +276,7 @@ func newFramer(conn net.Conn) *framer {
writer: bufio.NewWriterSize(conn, http2IOBufSize),
}
f.fr = http2.NewFramer(f.writer, f.reader)
+ f.fr.ReadMetaHeaders = hpack.NewDecoder(http2InitHeaderTableSize, nil)
return f
}
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/transport/http_util_test.go b/Godeps/_workspace/src/google.golang.org/grpc/transport/http_util_test.go
deleted file mode 100644
index b5b18bf4390..00000000000
--- a/Godeps/_workspace/src/google.golang.org/grpc/transport/http_util_test.go
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- *
- * Copyright 2014, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-package transport
-
-import (
- "fmt"
- "testing"
- "time"
-)
-
-func TestTimeoutEncode(t *testing.T) {
- for _, test := range []struct {
- in string
- out string
- }{
- {"12345678ns", "12345678n"},
- {"123456789ns", "123457u"},
- {"12345678us", "12345678u"},
- {"123456789us", "123457m"},
- {"12345678ms", "12345678m"},
- {"123456789ms", "123457S"},
- {"12345678s", "12345678S"},
- {"123456789s", "2057614M"},
- {"12345678m", "12345678M"},
- {"123456789m", "2057614H"},
- } {
- d, err := time.ParseDuration(test.in)
- if err != nil {
- t.Fatalf("failed to parse duration string %s: %v", test.in, err)
- }
- out := timeoutEncode(d)
- if out != test.out {
- t.Fatalf("timeoutEncode(%s) = %s, want %s", test.in, out, test.out)
- }
- }
-}
-
-func TestTimeoutDecode(t *testing.T) {
- for _, test := range []struct {
- // input
- s string
- // output
- d time.Duration
- err error
- }{
- {"1234S", time.Second * 1234, nil},
- {"1234x", 0, fmt.Errorf("transport: timeout unit is not recognized: %q", "1234x")},
- {"1", 0, fmt.Errorf("transport: timeout string is too short: %q", "1")},
- {"", 0, fmt.Errorf("transport: timeout string is too short: %q", "")},
- } {
- d, err := timeoutDecode(test.s)
- if d != test.d || fmt.Sprint(err) != fmt.Sprint(test.err) {
- t.Fatalf("timeoutDecode(%q) = %d, %v, want %d, %v", test.s, int64(d), err, int64(test.d), test.err)
- }
- }
-}
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/transport/testdata/ca.pem b/Godeps/_workspace/src/google.golang.org/grpc/transport/testdata/ca.pem
deleted file mode 100644
index 6c8511a73c6..00000000000
--- a/Godeps/_workspace/src/google.golang.org/grpc/transport/testdata/ca.pem
+++ /dev/null
@@ -1,15 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV
-BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
-aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla
-Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0
-YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT
-BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7
-+L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu
-g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd
-Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV
-HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau
-sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m
-oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG
-Dfcog5wrJytaQ6UA0wE=
------END CERTIFICATE-----
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/transport/testdata/server1.key b/Godeps/_workspace/src/google.golang.org/grpc/transport/testdata/server1.key
deleted file mode 100644
index 143a5b87658..00000000000
--- a/Godeps/_workspace/src/google.golang.org/grpc/transport/testdata/server1.key
+++ /dev/null
@@ -1,16 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOHDFScoLCVJpYDD
-M4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1BgzkWF+slf
-3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd9N8YwbBY
-AckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAECgYAn7qGnM2vbjJNBm0VZCkOkTIWm
-V10okw7EPJrdL2mkre9NasghNXbE1y5zDshx5Nt3KsazKOxTT8d0Jwh/3KbaN+YY
-tTCbKGW0pXDRBhwUHRcuRzScjli8Rih5UOCiZkhefUTcRb6xIhZJuQy71tjaSy0p
-dHZRmYyBYO2YEQ8xoQJBAPrJPhMBkzmEYFtyIEqAxQ/o/A6E+E4w8i+KM7nQCK7q
-K4JXzyXVAjLfyBZWHGM2uro/fjqPggGD6QH1qXCkI4MCQQDmdKeb2TrKRh5BY1LR
-81aJGKcJ2XbcDu6wMZK4oqWbTX2KiYn9GB0woM6nSr/Y6iy1u145YzYxEV/iMwff
-DJULAkB8B2MnyzOg0pNFJqBJuH29bKCcHa8gHJzqXhNO5lAlEbMK95p/P2Wi+4Hd
-aiEIAF1BF326QJcvYKmwSmrORp85AkAlSNxRJ50OWrfMZnBgzVjDx3xG6KsFQVk2
-ol6VhqL6dFgKUORFUWBvnKSyhjJxurlPEahV6oo6+A+mPhFY8eUvAkAZQyTdupP3
-XEFQKctGz+9+gKkemDp7LBBMEMBXrGTLPhpEfcjv/7KPdnFHYmhYeBTBnuVmTVWe
-F98XJ7tIFfJq
------END PRIVATE KEY-----
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/transport/testdata/server1.pem b/Godeps/_workspace/src/google.golang.org/grpc/transport/testdata/server1.pem
deleted file mode 100644
index f3d43fcc5be..00000000000
--- a/Godeps/_workspace/src/google.golang.org/grpc/transport/testdata/server1.pem
+++ /dev/null
@@ -1,16 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICnDCCAgWgAwIBAgIBBzANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJBVTET
-MBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ
-dHkgTHRkMQ8wDQYDVQQDEwZ0ZXN0Y2EwHhcNMTUxMTA0MDIyMDI0WhcNMjUxMTAx
-MDIyMDI0WjBlMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV
-BAcTB0NoaWNhZ28xFTATBgNVBAoTDEV4YW1wbGUsIENvLjEaMBgGA1UEAxQRKi50
-ZXN0Lmdvb2dsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOHDFSco
-LCVJpYDDM4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1Bg
-zkWF+slf3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd
-9N8YwbBYAckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAGjazBpMAkGA1UdEwQCMAAw
-CwYDVR0PBAQDAgXgME8GA1UdEQRIMEaCECoudGVzdC5nb29nbGUuZnKCGHdhdGVy
-em9vaS50ZXN0Lmdvb2dsZS5iZYISKi50ZXN0LnlvdXR1YmUuY29thwTAqAEDMA0G
-CSqGSIb3DQEBCwUAA4GBAJFXVifQNub1LUP4JlnX5lXNlo8FxZ2a12AFQs+bzoJ6
-hM044EDjqyxUqSbVePK0ni3w1fHQB5rY9yYC5f8G7aqqTY1QOhoUk8ZTSTRpnkTh
-y4jjdvTZeLDVBlueZUTDRmy2feY5aZIU18vFDK08dTG0A87pppuv1LNIR3loveU8
------END CERTIFICATE-----
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/transport/transport.go b/Godeps/_workspace/src/google.golang.org/grpc/transport/transport.go
index 86d2ae54c19..382a102fa5f 100644
--- a/Godeps/_workspace/src/google.golang.org/grpc/transport/transport.go
+++ b/Godeps/_workspace/src/google.golang.org/grpc/transport/transport.go
@@ -352,26 +352,40 @@ func NewClientTransport(target string, opts *ConnectOptions) (ClientTransport, e
// Options provides additional hints and information for message
// transmission.
type Options struct {
- // Indicate whether it is the last piece for this stream.
+ // Last indicates whether this write is the last piece for
+ // this stream.
Last bool
- // The hint to transport impl whether the data could be buffered for
- // batching write. Transport impl can feel free to ignore it.
+
+ // Delay is a hint to the transport implementation for whether
+ // the data could be buffered for a batching write. The
+ // Transport implementation may ignore the hint.
Delay bool
}
// CallHdr carries the information of a particular RPC.
type CallHdr struct {
- // Host specifies peer host.
+ // Host specifies the peer's host.
Host string
+
// Method specifies the operation to perform.
Method string
- // RecvCompress specifies the compression algorithm applied on inbound messages.
+
+ // RecvCompress specifies the compression algorithm applied on
+ // inbound messages.
RecvCompress string
- // SendCompress specifies the compression algorithm applied on outbound message.
+
+ // SendCompress specifies the compression algorithm applied on
+ // outbound message.
SendCompress string
+
+ // Flush indicates whether a new stream command should be sent
+ // to the peer without waiting for the first data. This is
+ // only a hint. The transport may modify the flush decision
+ // for performance purposes.
+ Flush bool
}
-// ClientTransport is the common interface for all gRPC client side transport
+// ClientTransport is the common interface for all gRPC client-side transport
// implementations.
type ClientTransport interface {
// Close tears down this transport. Once it returns, the transport
@@ -400,21 +414,33 @@ type ClientTransport interface {
Error() <-chan struct{}
}
-// ServerTransport is the common interface for all gRPC server side transport
+// ServerTransport is the common interface for all gRPC server-side transport
// implementations.
+//
+// Methods may be called concurrently from multiple goroutines, but
+// Write methods for a given Stream will be called serially.
type ServerTransport interface {
- // WriteStatus sends the status of a stream to the client.
- WriteStatus(s *Stream, statusCode codes.Code, statusDesc string) error
- // Write sends the data for the given stream.
- Write(s *Stream, data []byte, opts *Options) error
- // WriteHeader sends the header metedata for the given stream.
- WriteHeader(s *Stream, md metadata.MD) error
// HandleStreams receives incoming streams using the given handler.
HandleStreams(func(*Stream))
+
+ // WriteHeader sends the header metadata for the given stream.
+ // WriteHeader may not be called on all streams.
+ WriteHeader(s *Stream, md metadata.MD) error
+
+ // Write sends the data for the given stream.
+ // Write may not be called on all streams.
+ Write(s *Stream, data []byte, opts *Options) error
+
+ // WriteStatus sends the status of a stream to the client.
+ // WriteStatus is the final call made on a stream and always
+ // occurs.
+ WriteStatus(s *Stream, statusCode codes.Code, statusDesc string) error
+
// Close tears down the transport. Once it is called, the transport
// should not be accessed any more. All the pending streams and their
// handlers will be terminated asynchronously.
Close() error
+
// RemoteAddr returns the remote network address.
RemoteAddr() net.Addr
}
diff --git a/Godeps/_workspace/src/google.golang.org/grpc/transport/transport_test.go b/Godeps/_workspace/src/google.golang.org/grpc/transport/transport_test.go
deleted file mode 100644
index 707e63990bf..00000000000
--- a/Godeps/_workspace/src/google.golang.org/grpc/transport/transport_test.go
+++ /dev/null
@@ -1,659 +0,0 @@
-/*
- *
- * Copyright 2014, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-package transport
-
-import (
- "bytes"
- "io"
- "math"
- "net"
- "reflect"
- "strconv"
- "sync"
- "testing"
- "time"
-
- "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
- "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/http2"
- "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codes"
-)
-
-type server struct {
- lis net.Listener
- port string
- // channel to signal server is ready to serve.
- readyChan chan bool
- mu sync.Mutex
- conns map[ServerTransport]bool
-}
-
-var (
- expectedRequest = []byte("ping")
- expectedResponse = []byte("pong")
- expectedRequestLarge = make([]byte, initialWindowSize*2)
- expectedResponseLarge = make([]byte, initialWindowSize*2)
-)
-
-type testStreamHandler struct {
- t ServerTransport
-}
-
-type hType int
-
-const (
- normal hType = iota
- suspended
- misbehaved
-)
-
-func (h *testStreamHandler) handleStream(t *testing.T, s *Stream) {
- req := expectedRequest
- resp := expectedResponse
- if s.Method() == "foo.Large" {
- req = expectedRequestLarge
- resp = expectedResponseLarge
- }
- p := make([]byte, len(req))
- _, err := io.ReadFull(s, p)
- if err != nil {
- return
- }
- if !bytes.Equal(p, req) {
- t.Fatalf("handleStream got %v, want %v", p, req)
- }
- // send a response back to the client.
- h.t.Write(s, resp, &Options{})
- // send the trailer to end the stream.
- h.t.WriteStatus(s, codes.OK, "")
-}
-
-// handleStreamSuspension blocks until s.ctx is canceled.
-func (h *testStreamHandler) handleStreamSuspension(s *Stream) {
- go func() {
- <-s.ctx.Done()
- }()
-}
-
-func (h *testStreamHandler) handleStreamMisbehave(t *testing.T, s *Stream) {
- conn, ok := s.ServerTransport().(*http2Server)
- if !ok {
- t.Fatalf("Failed to convert %v to *http2Server", s.ServerTransport())
- }
- size := 1
- if s.Method() == "foo.MaxFrame" {
- size = http2MaxFrameLen
- }
- // Drain the client side stream flow control window.
- var sent int
- for sent <= initialWindowSize {
- <-conn.writableChan
- if err := conn.framer.writeData(true, s.id, false, make([]byte, size)); err != nil {
- conn.writableChan <- 0
- break
- }
- conn.writableChan <- 0
- sent += size
- }
-}
-
-// start starts server. Other goroutines should block on s.readyChan for futher operations.
-func (s *server) start(t *testing.T, port int, maxStreams uint32, ht hType) {
- var err error
- if port == 0 {
- s.lis, err = net.Listen("tcp", ":0")
- } else {
- s.lis, err = net.Listen("tcp", ":"+strconv.Itoa(port))
- }
- if err != nil {
- t.Fatalf("failed to listen: %v", err)
- }
- _, p, err := net.SplitHostPort(s.lis.Addr().String())
- if err != nil {
- t.Fatalf("failed to parse listener address: %v", err)
- }
- s.port = p
- s.conns = make(map[ServerTransport]bool)
- if s.readyChan != nil {
- close(s.readyChan)
- }
- for {
- conn, err := s.lis.Accept()
- if err != nil {
- return
- }
- transport, err := NewServerTransport("http2", conn, maxStreams, nil)
- if err != nil {
- return
- }
- s.mu.Lock()
- if s.conns == nil {
- s.mu.Unlock()
- transport.Close()
- return
- }
- s.conns[transport] = true
- s.mu.Unlock()
- h := &testStreamHandler{transport}
- switch ht {
- case suspended:
- go transport.HandleStreams(h.handleStreamSuspension)
- case misbehaved:
- go transport.HandleStreams(func(s *Stream) {
- go h.handleStreamMisbehave(t, s)
- })
- default:
- go transport.HandleStreams(func(s *Stream) {
- go h.handleStream(t, s)
- })
- }
- }
-}
-
-func (s *server) wait(t *testing.T, timeout time.Duration) {
- select {
- case <-s.readyChan:
- case <-time.After(timeout):
- t.Fatalf("Timed out after %v waiting for server to be ready", timeout)
- }
-}
-
-func (s *server) stop() {
- s.lis.Close()
- s.mu.Lock()
- for c := range s.conns {
- c.Close()
- }
- s.conns = nil
- s.mu.Unlock()
-}
-
-func setUp(t *testing.T, port int, maxStreams uint32, ht hType) (*server, ClientTransport) {
- server := &server{readyChan: make(chan bool)}
- go server.start(t, port, maxStreams, ht)
- server.wait(t, 2*time.Second)
- addr := "localhost:" + server.port
- var (
- ct ClientTransport
- connErr error
- )
- ct, connErr = NewClientTransport(addr, &ConnectOptions{})
- if connErr != nil {
- t.Fatalf("failed to create transport: %v", connErr)
- }
- return server, ct
-}
-
-func TestClientSendAndReceive(t *testing.T) {
- server, ct := setUp(t, 0, math.MaxUint32, normal)
- callHdr := &CallHdr{
- Host: "localhost",
- Method: "foo.Small",
- }
- s1, err1 := ct.NewStream(context.Background(), callHdr)
- if err1 != nil {
- t.Fatalf("failed to open stream: %v", err1)
- }
- if s1.id != 1 {
- t.Fatalf("wrong stream id: %d", s1.id)
- }
- s2, err2 := ct.NewStream(context.Background(), callHdr)
- if err2 != nil {
- t.Fatalf("failed to open stream: %v", err2)
- }
- if s2.id != 3 {
- t.Fatalf("wrong stream id: %d", s2.id)
- }
- opts := Options{
- Last: true,
- Delay: false,
- }
- if err := ct.Write(s1, expectedRequest, &opts); err != nil {
- t.Fatalf("failed to send data: %v", err)
- }
- p := make([]byte, len(expectedResponse))
- _, recvErr := io.ReadFull(s1, p)
- if recvErr != nil || !bytes.Equal(p, expectedResponse) {
- t.Fatalf("Error: %v, want ; Result: %v, want %v", recvErr, p, expectedResponse)
- }
- _, recvErr = io.ReadFull(s1, p)
- if recvErr != io.EOF {
- t.Fatalf("Error: %v; want ", recvErr)
- }
- ct.Close()
- server.stop()
-}
-
-func TestClientErrorNotify(t *testing.T) {
- server, ct := setUp(t, 0, math.MaxUint32, normal)
- go server.stop()
- // ct.reader should detect the error and activate ct.Error().
- <-ct.Error()
- ct.Close()
-}
-
-func performOneRPC(ct ClientTransport) {
- callHdr := &CallHdr{
- Host: "localhost",
- Method: "foo.Small",
- }
- s, err := ct.NewStream(context.Background(), callHdr)
- if err != nil {
- return
- }
- opts := Options{
- Last: true,
- Delay: false,
- }
- if err := ct.Write(s, expectedRequest, &opts); err == nil {
- time.Sleep(5 * time.Millisecond)
- // The following s.Recv()'s could error out because the
- // underlying transport is gone.
- //
- // Read response
- p := make([]byte, len(expectedResponse))
- io.ReadFull(s, p)
- // Read io.EOF
- io.ReadFull(s, p)
- }
-}
-
-func TestClientMix(t *testing.T) {
- s, ct := setUp(t, 0, math.MaxUint32, normal)
- go func(s *server) {
- time.Sleep(5 * time.Second)
- s.stop()
- }(s)
- go func(ct ClientTransport) {
- <-ct.Error()
- ct.Close()
- }(ct)
- for i := 0; i < 1000; i++ {
- time.Sleep(10 * time.Millisecond)
- go performOneRPC(ct)
- }
-}
-
-func TestLargeMessage(t *testing.T) {
- server, ct := setUp(t, 0, math.MaxUint32, normal)
- callHdr := &CallHdr{
- Host: "localhost",
- Method: "foo.Large",
- }
- var wg sync.WaitGroup
- for i := 0; i < 2; i++ {
- wg.Add(1)
- go func() {
- defer wg.Done()
- s, err := ct.NewStream(context.Background(), callHdr)
- if err != nil {
- t.Errorf("failed to open stream: %v", err)
- }
- if err := ct.Write(s, expectedRequestLarge, &Options{Last: true, Delay: false}); err != nil {
- t.Errorf("failed to send data: %v", err)
- }
- p := make([]byte, len(expectedResponseLarge))
- _, recvErr := io.ReadFull(s, p)
- if recvErr != nil || !bytes.Equal(p, expectedResponseLarge) {
- t.Errorf("Error: %v, want ; Result len: %d, want len %d", recvErr, len(p), len(expectedResponseLarge))
- }
- _, recvErr = io.ReadFull(s, p)
- if recvErr != io.EOF {
- t.Errorf("Error: %v; want ", recvErr)
- }
- }()
- }
- wg.Wait()
- ct.Close()
- server.stop()
-}
-
-func TestLargeMessageSuspension(t *testing.T) {
- server, ct := setUp(t, 0, math.MaxUint32, suspended)
- callHdr := &CallHdr{
- Host: "localhost",
- Method: "foo.Large",
- }
- // Set a long enough timeout for writing a large message out.
- ctx, _ := context.WithTimeout(context.Background(), time.Second)
- s, err := ct.NewStream(ctx, callHdr)
- if err != nil {
- t.Fatalf("failed to open stream: %v", err)
- }
- // Write should not be done successfully due to flow control.
- err = ct.Write(s, expectedRequestLarge, &Options{Last: true, Delay: false})
- expectedErr := StreamErrorf(codes.DeadlineExceeded, "%v", context.DeadlineExceeded)
- if err == nil || err != expectedErr {
- t.Fatalf("Write got %v, want %v", err, expectedErr)
- }
- ct.Close()
- server.stop()
-}
-
-func TestMaxStreams(t *testing.T) {
- server, ct := setUp(t, 0, 1, suspended)
- callHdr := &CallHdr{
- Host: "localhost",
- Method: "foo.Large",
- }
- // Have a pending stream which takes all streams quota.
- s, err := ct.NewStream(context.Background(), callHdr)
- if err != nil {
- t.Fatalf("Failed to open stream: %v", err)
- }
- cc, ok := ct.(*http2Client)
- if !ok {
- t.Fatalf("Failed to convert %v to *http2Client", ct)
- }
- done := make(chan struct{})
- ch := make(chan int)
- go func() {
- for {
- select {
- case <-time.After(5 * time.Millisecond):
- ch <- 0
- case <-time.After(5 * time.Second):
- close(done)
- return
- }
- }
- }()
- for {
- select {
- case <-ch:
- case <-done:
- t.Fatalf("Client has not received the max stream setting in 5 seconds.")
- }
- cc.mu.Lock()
- // cc.streamsQuota should be initialized once receiving the 1st setting frame from
- // the server.
- if cc.streamsQuota != nil {
- cc.mu.Unlock()
- select {
- case <-cc.streamsQuota.acquire():
- t.Fatalf("streamsQuota.acquire() becomes readable mistakenly.")
- default:
- if cc.streamsQuota.quota != 0 {
- t.Fatalf("streamsQuota.quota got non-zero quota mistakenly.")
- }
- }
- break
- }
- cc.mu.Unlock()
- }
- // Close the pending stream so that the streams quota becomes available for the next new stream.
- ct.CloseStream(s, nil)
- select {
- case i := <-cc.streamsQuota.acquire():
- if i != 1 {
- t.Fatalf("streamsQuota.acquire() got %d quota, want 1.", i)
- }
- cc.streamsQuota.add(i)
- default:
- t.Fatalf("streamsQuota.acquire() is not readable.")
- }
- if _, err := ct.NewStream(context.Background(), callHdr); err != nil {
- t.Fatalf("Failed to open stream: %v", err)
- }
- ct.Close()
- server.stop()
-}
-
-func TestServerContextCanceledOnClosedConnection(t *testing.T) {
- server, ct := setUp(t, 0, math.MaxUint32, suspended)
- callHdr := &CallHdr{
- Host: "localhost",
- Method: "foo",
- }
- var sc *http2Server
- // Wait until the server transport is setup.
- for {
- server.mu.Lock()
- if len(server.conns) == 0 {
- server.mu.Unlock()
- time.Sleep(time.Millisecond)
- continue
- }
- for k := range server.conns {
- var ok bool
- sc, ok = k.(*http2Server)
- if !ok {
- t.Fatalf("Failed to convert %v to *http2Server", k)
- }
- }
- server.mu.Unlock()
- break
- }
- cc, ok := ct.(*http2Client)
- if !ok {
- t.Fatalf("Failed to convert %v to *http2Client", ct)
- }
- s, err := ct.NewStream(context.Background(), callHdr)
- if err != nil {
- t.Fatalf("Failed to open stream: %v", err)
- }
- // Make sure the headers frame is flushed out.
- <-cc.writableChan
- if err = cc.framer.writeData(true, s.id, false, make([]byte, http2MaxFrameLen)); err != nil {
- t.Fatalf("Failed to write data: %v", err)
- }
- cc.writableChan <- 0
- // Loop until the server side stream is created.
- var ss *Stream
- for {
- time.Sleep(time.Second)
- sc.mu.Lock()
- if len(sc.activeStreams) == 0 {
- sc.mu.Unlock()
- continue
- }
- ss = sc.activeStreams[s.id]
- sc.mu.Unlock()
- break
- }
- cc.Close()
- select {
- case <-ss.Context().Done():
- if ss.Context().Err() != context.Canceled {
- t.Fatalf("ss.Context().Err() got %v, want %v", ss.Context().Err(), context.Canceled)
- }
- case <-time.After(5 * time.Second):
- t.Fatalf("Failed to cancel the context of the sever side stream.")
- }
-}
-
-func TestServerWithMisbehavedClient(t *testing.T) {
- server, ct := setUp(t, 0, math.MaxUint32, suspended)
- callHdr := &CallHdr{
- Host: "localhost",
- Method: "foo",
- }
- var sc *http2Server
- // Wait until the server transport is setup.
- for {
- server.mu.Lock()
- if len(server.conns) == 0 {
- server.mu.Unlock()
- time.Sleep(time.Millisecond)
- continue
- }
- for k := range server.conns {
- var ok bool
- sc, ok = k.(*http2Server)
- if !ok {
- t.Fatalf("Failed to convert %v to *http2Server", k)
- }
- }
- server.mu.Unlock()
- break
- }
- cc, ok := ct.(*http2Client)
- if !ok {
- t.Fatalf("Failed to convert %v to *http2Client", ct)
- }
- // Test server behavior for violation of stream flow control window size restriction.
- s, err := ct.NewStream(context.Background(), callHdr)
- if err != nil {
- t.Fatalf("Failed to open stream: %v", err)
- }
- var sent int
- // Drain the stream flow control window
- <-cc.writableChan
- if err = cc.framer.writeData(true, s.id, false, make([]byte, http2MaxFrameLen)); err != nil {
- t.Fatalf("Failed to write data: %v", err)
- }
- cc.writableChan <- 0
- sent += http2MaxFrameLen
- // Wait until the server creates the corresponding stream and receive some data.
- var ss *Stream
- for {
- time.Sleep(time.Millisecond)
- sc.mu.Lock()
- if len(sc.activeStreams) == 0 {
- sc.mu.Unlock()
- continue
- }
- ss = sc.activeStreams[s.id]
- sc.mu.Unlock()
- ss.fc.mu.Lock()
- if ss.fc.pendingData > 0 {
- ss.fc.mu.Unlock()
- break
- }
- ss.fc.mu.Unlock()
- }
- if ss.fc.pendingData != http2MaxFrameLen || ss.fc.pendingUpdate != 0 || sc.fc.pendingData != http2MaxFrameLen || sc.fc.pendingUpdate != 0 {
- t.Fatalf("Server mistakenly updates inbound flow control params: got %d, %d, %d, %d; want %d, %d, %d, %d", ss.fc.pendingData, ss.fc.pendingUpdate, sc.fc.pendingData, sc.fc.pendingUpdate, http2MaxFrameLen, 0, http2MaxFrameLen, 0)
- }
- // Keep sending until the server inbound window is drained for that stream.
- for sent <= initialWindowSize {
- <-cc.writableChan
- if err = cc.framer.writeData(true, s.id, false, make([]byte, 1)); err != nil {
- t.Fatalf("Failed to write data: %v", err)
- }
- cc.writableChan <- 0
- sent++
- }
- // Server sent a resetStream for s already.
- code := http2RSTErrConvTab[http2.ErrCodeFlowControl]
- if _, err := io.ReadFull(s, make([]byte, 1)); err != io.EOF || s.statusCode != code {
- t.Fatalf("%v got err %v with statusCode %d, want err with statusCode %d", s, err, s.statusCode, code)
- }
-
- if ss.fc.pendingData != 0 || ss.fc.pendingUpdate != 0 || sc.fc.pendingData != 0 || sc.fc.pendingUpdate != initialWindowSize {
- t.Fatalf("Server mistakenly resets inbound flow control params: got %d, %d, %d, %d; want 0, 0, 0, %d", ss.fc.pendingData, ss.fc.pendingUpdate, sc.fc.pendingData, sc.fc.pendingUpdate, initialWindowSize)
- }
- ct.CloseStream(s, nil)
- // Test server behavior for violation of connection flow control window size restriction.
- //
- // Keep creating new streams until the connection window is drained on the server and
- // the server tears down the connection.
- for {
- s, err := ct.NewStream(context.Background(), callHdr)
- if err != nil {
- // The server tears down the connection.
- break
- }
- <-cc.writableChan
- cc.framer.writeData(true, s.id, true, make([]byte, http2MaxFrameLen))
- cc.writableChan <- 0
- }
- ct.Close()
- server.stop()
-}
-
-func TestClientWithMisbehavedServer(t *testing.T) {
- server, ct := setUp(t, 0, math.MaxUint32, misbehaved)
- callHdr := &CallHdr{
- Host: "localhost",
- Method: "foo",
- }
- conn, ok := ct.(*http2Client)
- if !ok {
- t.Fatalf("Failed to convert %v to *http2Client", ct)
- }
- // Test the logic for the violation of stream flow control window size restriction.
- s, err := ct.NewStream(context.Background(), callHdr)
- if err != nil {
- t.Fatalf("Failed to open stream: %v", err)
- }
- if err := ct.Write(s, expectedRequest, &Options{Last: true, Delay: false}); err != nil {
- t.Fatalf("Failed to write: %v", err)
- }
- // Read without window update.
- for {
- p := make([]byte, http2MaxFrameLen)
- if _, err = s.dec.Read(p); err != nil {
- break
- }
- }
- if s.fc.pendingData != initialWindowSize || s.fc.pendingUpdate != 0 || conn.fc.pendingData != initialWindowSize || conn.fc.pendingUpdate != 0 {
- t.Fatalf("Client mistakenly updates inbound flow control params: got %d, %d, %d, %d; want %d, %d, %d, %d", s.fc.pendingData, s.fc.pendingUpdate, conn.fc.pendingData, conn.fc.pendingUpdate, initialWindowSize, 0, initialWindowSize, 0)
- }
- if err != io.EOF || s.statusCode != codes.Internal {
- t.Fatalf("Got err %v and the status code %d, want and the code %d", err, s.statusCode, codes.Internal)
- }
- conn.CloseStream(s, err)
- if s.fc.pendingData != 0 || s.fc.pendingUpdate != 0 || conn.fc.pendingData != 0 || conn.fc.pendingUpdate != initialWindowSize {
- t.Fatalf("Client mistakenly resets inbound flow control params: got %d, %d, %d, %d; want 0, 0, 0, %d", s.fc.pendingData, s.fc.pendingUpdate, conn.fc.pendingData, conn.fc.pendingUpdate, initialWindowSize)
- }
- // Test the logic for the violation of the connection flow control window size restriction.
- //
- // Generate enough streams to drain the connection window.
- callHdr = &CallHdr{
- Host: "localhost",
- Method: "foo.MaxFrame",
- }
- for i := 0; i < int(initialConnWindowSize/initialWindowSize+10); i++ {
- s, err := ct.NewStream(context.Background(), callHdr)
- if err != nil {
- break
- }
- if err := ct.Write(s, expectedRequest, &Options{Last: true, Delay: false}); err != nil {
- break
- }
- }
- // http2Client.errChan is closed due to connection flow control window size violation.
- <-conn.Error()
- ct.Close()
- server.stop()
-}
-
-func TestStreamContext(t *testing.T) {
- expectedStream := Stream{}
- ctx := newContextWithStream(context.Background(), &expectedStream)
- s, ok := StreamFromContext(ctx)
- if !ok || !reflect.DeepEqual(expectedStream, *s) {
- t.Fatalf("GetStreamFromContext(%v) = %v, %t, want: %v, true", ctx, *s, ok, expectedStream)
- }
-}