Add command execution/port forwarding dependencies
code.google.com/p/go.net/spdy github.com/docker/docker/pkg/term github.com/docker/spdystream github.com/kr/pty
This commit is contained in:
19
Godeps/Godeps.json
generated
19
Godeps/Godeps.json
generated
@@ -14,6 +14,11 @@
|
|||||||
"Comment": "null-12",
|
"Comment": "null-12",
|
||||||
"Rev": "7dda39b2e7d5e265014674c5af696ba4186679e9"
|
"Rev": "7dda39b2e7d5e265014674c5af696ba4186679e9"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "code.google.com/p/go.net/spdy",
|
||||||
|
"Comment": "null-240",
|
||||||
|
"Rev": "937a34c9de13c766c814510f76bca091dee06028"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "code.google.com/p/google-api-go-client/compute/v1",
|
"ImportPath": "code.google.com/p/google-api-go-client/compute/v1",
|
||||||
"Comment": "release-96",
|
"Comment": "release-96",
|
||||||
@@ -83,6 +88,11 @@
|
|||||||
"Comment": "v1.4.1-108-g364720b",
|
"Comment": "v1.4.1-108-g364720b",
|
||||||
"Rev": "211513156dc1ace48e630b4bf4ea0fcfdc8d9abf"
|
"Rev": "211513156dc1ace48e630b4bf4ea0fcfdc8d9abf"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/docker/docker/pkg/term",
|
||||||
|
"Comment": "v1.4.1-108-g364720b",
|
||||||
|
"Rev": "364720b5e7e725cdc466171de873eefdb8609a33"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/docker/docker/pkg/units",
|
"ImportPath": "github.com/docker/docker/pkg/units",
|
||||||
"Comment": "v1.4.1-108-g364720b",
|
"Comment": "v1.4.1-108-g364720b",
|
||||||
@@ -93,6 +103,10 @@
|
|||||||
"Comment": "v1.4.1-108-g364720b",
|
"Comment": "v1.4.1-108-g364720b",
|
||||||
"Rev": "211513156dc1ace48e630b4bf4ea0fcfdc8d9abf"
|
"Rev": "211513156dc1ace48e630b4bf4ea0fcfdc8d9abf"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/docker/spdystream",
|
||||||
|
"Rev": "29e1da2890f60336f98d0b3bf28b05070aa2ee4d"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/elazarl/go-bindata-assetfs",
|
"ImportPath": "github.com/elazarl/go-bindata-assetfs",
|
||||||
"Rev": "ae4665cf2d188c65764c73fe4af5378acc549510"
|
"Rev": "ae4665cf2d188c65764c73fe4af5378acc549510"
|
||||||
@@ -145,6 +159,11 @@
|
|||||||
"ImportPath": "github.com/matttproud/golang_protobuf_extensions/ext",
|
"ImportPath": "github.com/matttproud/golang_protobuf_extensions/ext",
|
||||||
"Rev": "7a864a042e844af638df17ebbabf8183dace556a"
|
"Rev": "7a864a042e844af638df17ebbabf8183dace556a"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/kr/pty",
|
||||||
|
"Comment": "release.r56-25-g05017fc",
|
||||||
|
"Rev": "05017fcccf23c823bfdea560dcc958a136e54fb7"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/miekg/dns",
|
"ImportPath": "github.com/miekg/dns",
|
||||||
"Rev": "3f504e8dabd5d562e997d19ce0200aa41973e1b2"
|
"Rev": "3f504e8dabd5d562e997d19ce0200aa41973e1b2"
|
||||||
|
187
Godeps/_workspace/src/code.google.com/p/go.net/spdy/dictionary.go
generated
vendored
Normal file
187
Godeps/_workspace/src/code.google.com/p/go.net/spdy/dictionary.go
generated
vendored
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package spdy
|
||||||
|
|
||||||
|
// headerDictionary is the dictionary sent to the zlib compressor/decompressor.
|
||||||
|
var headerDictionary = []byte{
|
||||||
|
0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69,
|
||||||
|
0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68,
|
||||||
|
0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70,
|
||||||
|
0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70,
|
||||||
|
0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65,
|
||||||
|
0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05,
|
||||||
|
0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00,
|
||||||
|
0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00,
|
||||||
|
0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70,
|
||||||
|
0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65,
|
||||||
|
0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63,
|
||||||
|
0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f,
|
||||||
|
0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f,
|
||||||
|
0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c,
|
||||||
|
0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00,
|
||||||
|
0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70,
|
||||||
|
0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73,
|
||||||
|
0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00,
|
||||||
|
0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77,
|
||||||
|
0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68,
|
||||||
|
0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f,
|
||||||
|
0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63,
|
||||||
|
0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72,
|
||||||
|
0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f,
|
||||||
|
0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
|
||||||
|
0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74,
|
||||||
|
0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65,
|
||||||
|
0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74,
|
||||||
|
0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f,
|
||||||
|
0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10,
|
||||||
|
0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d,
|
||||||
|
0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65,
|
||||||
|
0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74,
|
||||||
|
0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67,
|
||||||
|
0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f,
|
||||||
|
0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f,
|
||||||
|
0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00,
|
||||||
|
0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
|
||||||
|
0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00,
|
||||||
|
0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
|
||||||
|
0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00,
|
||||||
|
0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
|
||||||
|
0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00,
|
||||||
|
0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00,
|
||||||
|
0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00,
|
||||||
|
0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74,
|
||||||
|
0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69,
|
||||||
|
0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66,
|
||||||
|
0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68,
|
||||||
|
0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69,
|
||||||
|
0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00,
|
||||||
|
0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f,
|
||||||
|
0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73,
|
||||||
|
0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d,
|
||||||
|
0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d,
|
||||||
|
0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00,
|
||||||
|
0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67,
|
||||||
|
0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d,
|
||||||
|
0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69,
|
||||||
|
0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65,
|
||||||
|
0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74,
|
||||||
|
0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65,
|
||||||
|
0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63,
|
||||||
|
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00,
|
||||||
|
0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72,
|
||||||
|
0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00,
|
||||||
|
0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00,
|
||||||
|
0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79,
|
||||||
|
0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74,
|
||||||
|
0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00,
|
||||||
|
0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61,
|
||||||
|
0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61,
|
||||||
|
0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05,
|
||||||
|
0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00,
|
||||||
|
0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72,
|
||||||
|
0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72,
|
||||||
|
0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00,
|
||||||
|
0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65,
|
||||||
|
0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00,
|
||||||
|
0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c,
|
||||||
|
0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72,
|
||||||
|
0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65,
|
||||||
|
0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00,
|
||||||
|
0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61,
|
||||||
|
0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73,
|
||||||
|
0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74,
|
||||||
|
0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79,
|
||||||
|
0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00,
|
||||||
|
0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69,
|
||||||
|
0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77,
|
||||||
|
0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e,
|
||||||
|
0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00,
|
||||||
|
0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64,
|
||||||
|
0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00,
|
||||||
|
0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,
|
||||||
|
0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30,
|
||||||
|
0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76,
|
||||||
|
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00,
|
||||||
|
0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31,
|
||||||
|
0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72,
|
||||||
|
0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62,
|
||||||
|
0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73,
|
||||||
|
0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69,
|
||||||
|
0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65,
|
||||||
|
0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00,
|
||||||
|
0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69,
|
||||||
|
0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32,
|
||||||
|
0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35,
|
||||||
|
0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30,
|
||||||
|
0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33,
|
||||||
|
0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37,
|
||||||
|
0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30,
|
||||||
|
0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34,
|
||||||
|
0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31,
|
||||||
|
0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31,
|
||||||
|
0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34,
|
||||||
|
0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34,
|
||||||
|
0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e,
|
||||||
|
0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f,
|
||||||
|
0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65,
|
||||||
|
0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61,
|
||||||
|
0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20,
|
||||||
|
0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65,
|
||||||
|
0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f,
|
||||||
|
0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d,
|
||||||
|
0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34,
|
||||||
|
0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52,
|
||||||
|
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30,
|
||||||
|
0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68,
|
||||||
|
0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30,
|
||||||
|
0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64,
|
||||||
|
0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e,
|
||||||
|
0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64,
|
||||||
|
0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65,
|
||||||
|
0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72,
|
||||||
|
0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f,
|
||||||
|
0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74,
|
||||||
|
0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65,
|
||||||
|
0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20,
|
||||||
|
0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20,
|
||||||
|
0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61,
|
||||||
|
0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46,
|
||||||
|
0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41,
|
||||||
|
0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a,
|
||||||
|
0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41,
|
||||||
|
0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20,
|
||||||
|
0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20,
|
||||||
|
0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30,
|
||||||
|
0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e,
|
||||||
|
0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57,
|
||||||
|
0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c,
|
||||||
|
0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61,
|
||||||
|
0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20,
|
||||||
|
0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b,
|
||||||
|
0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f,
|
||||||
|
0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61,
|
||||||
|
0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69,
|
||||||
|
0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67,
|
||||||
|
0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67,
|
||||||
|
0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69,
|
||||||
|
0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78,
|
||||||
|
0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69,
|
||||||
|
0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78,
|
||||||
|
0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c,
|
||||||
|
0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c,
|
||||||
|
0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74,
|
||||||
|
0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72,
|
||||||
|
0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c,
|
||||||
|
0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74,
|
||||||
|
0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65,
|
||||||
|
0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65,
|
||||||
|
0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64,
|
||||||
|
0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65,
|
||||||
|
0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63,
|
||||||
|
0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69,
|
||||||
|
0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d,
|
||||||
|
0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a,
|
||||||
|
0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e,
|
||||||
|
}
|
348
Godeps/_workspace/src/code.google.com/p/go.net/spdy/read.go
generated
vendored
Normal file
348
Godeps/_workspace/src/code.google.com/p/go.net/spdy/read.go
generated
vendored
Normal file
@@ -0,0 +1,348 @@
|
|||||||
|
// Copyright 2011 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 spdy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"compress/zlib"
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (frame *SynStreamFrame) read(h ControlFrameHeader, f *Framer) error {
|
||||||
|
return f.readSynStreamFrame(h, frame)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (frame *SynReplyFrame) read(h ControlFrameHeader, f *Framer) error {
|
||||||
|
return f.readSynReplyFrame(h, frame)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (frame *RstStreamFrame) read(h ControlFrameHeader, f *Framer) error {
|
||||||
|
frame.CFHeader = h
|
||||||
|
if err := binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := binary.Read(f.r, binary.BigEndian, &frame.Status); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if frame.Status == 0 {
|
||||||
|
return &Error{InvalidControlFrame, frame.StreamId}
|
||||||
|
}
|
||||||
|
if frame.StreamId == 0 {
|
||||||
|
return &Error{ZeroStreamId, 0}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (frame *SettingsFrame) read(h ControlFrameHeader, f *Framer) error {
|
||||||
|
frame.CFHeader = h
|
||||||
|
var numSettings uint32
|
||||||
|
if err := binary.Read(f.r, binary.BigEndian, &numSettings); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
frame.FlagIdValues = make([]SettingsFlagIdValue, numSettings)
|
||||||
|
for i := uint32(0); i < numSettings; i++ {
|
||||||
|
if err := binary.Read(f.r, binary.BigEndian, &frame.FlagIdValues[i].Id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
frame.FlagIdValues[i].Flag = SettingsFlag((frame.FlagIdValues[i].Id & 0xff000000) >> 24)
|
||||||
|
frame.FlagIdValues[i].Id &= 0xffffff
|
||||||
|
if err := binary.Read(f.r, binary.BigEndian, &frame.FlagIdValues[i].Value); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (frame *PingFrame) read(h ControlFrameHeader, f *Framer) error {
|
||||||
|
frame.CFHeader = h
|
||||||
|
if err := binary.Read(f.r, binary.BigEndian, &frame.Id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if frame.Id == 0 {
|
||||||
|
return &Error{ZeroStreamId, 0}
|
||||||
|
}
|
||||||
|
if frame.CFHeader.Flags != 0 {
|
||||||
|
return &Error{InvalidControlFrame, StreamId(frame.Id)}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (frame *GoAwayFrame) read(h ControlFrameHeader, f *Framer) error {
|
||||||
|
frame.CFHeader = h
|
||||||
|
if err := binary.Read(f.r, binary.BigEndian, &frame.LastGoodStreamId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if frame.CFHeader.Flags != 0 {
|
||||||
|
return &Error{InvalidControlFrame, frame.LastGoodStreamId}
|
||||||
|
}
|
||||||
|
if frame.CFHeader.length != 8 {
|
||||||
|
return &Error{InvalidControlFrame, frame.LastGoodStreamId}
|
||||||
|
}
|
||||||
|
if err := binary.Read(f.r, binary.BigEndian, &frame.Status); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (frame *HeadersFrame) read(h ControlFrameHeader, f *Framer) error {
|
||||||
|
return f.readHeadersFrame(h, frame)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (frame *WindowUpdateFrame) read(h ControlFrameHeader, f *Framer) error {
|
||||||
|
frame.CFHeader = h
|
||||||
|
if err := binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if frame.CFHeader.Flags != 0 {
|
||||||
|
return &Error{InvalidControlFrame, frame.StreamId}
|
||||||
|
}
|
||||||
|
if frame.CFHeader.length != 8 {
|
||||||
|
return &Error{InvalidControlFrame, frame.StreamId}
|
||||||
|
}
|
||||||
|
if err := binary.Read(f.r, binary.BigEndian, &frame.DeltaWindowSize); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newControlFrame(frameType ControlFrameType) (controlFrame, error) {
|
||||||
|
ctor, ok := cframeCtor[frameType]
|
||||||
|
if !ok {
|
||||||
|
return nil, &Error{Err: InvalidControlFrame}
|
||||||
|
}
|
||||||
|
return ctor(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var cframeCtor = map[ControlFrameType]func() controlFrame{
|
||||||
|
TypeSynStream: func() controlFrame { return new(SynStreamFrame) },
|
||||||
|
TypeSynReply: func() controlFrame { return new(SynReplyFrame) },
|
||||||
|
TypeRstStream: func() controlFrame { return new(RstStreamFrame) },
|
||||||
|
TypeSettings: func() controlFrame { return new(SettingsFrame) },
|
||||||
|
TypePing: func() controlFrame { return new(PingFrame) },
|
||||||
|
TypeGoAway: func() controlFrame { return new(GoAwayFrame) },
|
||||||
|
TypeHeaders: func() controlFrame { return new(HeadersFrame) },
|
||||||
|
TypeWindowUpdate: func() controlFrame { return new(WindowUpdateFrame) },
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Framer) uncorkHeaderDecompressor(payloadSize int64) error {
|
||||||
|
if f.headerDecompressor != nil {
|
||||||
|
f.headerReader.N = payloadSize
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
f.headerReader = io.LimitedReader{R: f.r, N: payloadSize}
|
||||||
|
decompressor, err := zlib.NewReaderDict(&f.headerReader, []byte(headerDictionary))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f.headerDecompressor = decompressor
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadFrame reads SPDY encoded data and returns a decompressed Frame.
|
||||||
|
func (f *Framer) ReadFrame() (Frame, error) {
|
||||||
|
var firstWord uint32
|
||||||
|
if err := binary.Read(f.r, binary.BigEndian, &firstWord); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if firstWord&0x80000000 != 0 {
|
||||||
|
frameType := ControlFrameType(firstWord & 0xffff)
|
||||||
|
version := uint16(firstWord >> 16 & 0x7fff)
|
||||||
|
return f.parseControlFrame(version, frameType)
|
||||||
|
}
|
||||||
|
return f.parseDataFrame(StreamId(firstWord & 0x7fffffff))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Framer) parseControlFrame(version uint16, frameType ControlFrameType) (Frame, error) {
|
||||||
|
var length uint32
|
||||||
|
if err := binary.Read(f.r, binary.BigEndian, &length); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
flags := ControlFlags((length & 0xff000000) >> 24)
|
||||||
|
length &= 0xffffff
|
||||||
|
header := ControlFrameHeader{version, frameType, flags, length}
|
||||||
|
cframe, err := newControlFrame(frameType)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = cframe.read(header, f); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return cframe, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseHeaderValueBlock(r io.Reader, streamId StreamId) (http.Header, error) {
|
||||||
|
var numHeaders uint32
|
||||||
|
if err := binary.Read(r, binary.BigEndian, &numHeaders); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var e error
|
||||||
|
h := make(http.Header, int(numHeaders))
|
||||||
|
for i := 0; i < int(numHeaders); i++ {
|
||||||
|
var length uint32
|
||||||
|
if err := binary.Read(r, binary.BigEndian, &length); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
nameBytes := make([]byte, length)
|
||||||
|
if _, err := io.ReadFull(r, nameBytes); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
name := string(nameBytes)
|
||||||
|
if name != strings.ToLower(name) {
|
||||||
|
e = &Error{UnlowercasedHeaderName, streamId}
|
||||||
|
name = strings.ToLower(name)
|
||||||
|
}
|
||||||
|
if h[name] != nil {
|
||||||
|
e = &Error{DuplicateHeaders, streamId}
|
||||||
|
}
|
||||||
|
if err := binary.Read(r, binary.BigEndian, &length); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
value := make([]byte, length)
|
||||||
|
if _, err := io.ReadFull(r, value); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
valueList := strings.Split(string(value), headerValueSeparator)
|
||||||
|
for _, v := range valueList {
|
||||||
|
h.Add(name, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if e != nil {
|
||||||
|
return h, e
|
||||||
|
}
|
||||||
|
return h, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Framer) readSynStreamFrame(h ControlFrameHeader, frame *SynStreamFrame) error {
|
||||||
|
frame.CFHeader = h
|
||||||
|
var err error
|
||||||
|
if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = binary.Read(f.r, binary.BigEndian, &frame.AssociatedToStreamId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = binary.Read(f.r, binary.BigEndian, &frame.Priority); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
frame.Priority >>= 5
|
||||||
|
if err = binary.Read(f.r, binary.BigEndian, &frame.Slot); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
reader := f.r
|
||||||
|
if !f.headerCompressionDisabled {
|
||||||
|
err := f.uncorkHeaderDecompressor(int64(h.length - 10))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
reader = f.headerDecompressor
|
||||||
|
}
|
||||||
|
frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId)
|
||||||
|
if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) {
|
||||||
|
err = &Error{WrongCompressedPayloadSize, 0}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for h := range frame.Headers {
|
||||||
|
if invalidReqHeaders[h] {
|
||||||
|
return &Error{InvalidHeaderPresent, frame.StreamId}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if frame.StreamId == 0 {
|
||||||
|
return &Error{ZeroStreamId, 0}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Framer) readSynReplyFrame(h ControlFrameHeader, frame *SynReplyFrame) error {
|
||||||
|
frame.CFHeader = h
|
||||||
|
var err error
|
||||||
|
if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
reader := f.r
|
||||||
|
if !f.headerCompressionDisabled {
|
||||||
|
err := f.uncorkHeaderDecompressor(int64(h.length - 4))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
reader = f.headerDecompressor
|
||||||
|
}
|
||||||
|
frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId)
|
||||||
|
if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) {
|
||||||
|
err = &Error{WrongCompressedPayloadSize, 0}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for h := range frame.Headers {
|
||||||
|
if invalidRespHeaders[h] {
|
||||||
|
return &Error{InvalidHeaderPresent, frame.StreamId}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if frame.StreamId == 0 {
|
||||||
|
return &Error{ZeroStreamId, 0}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Framer) readHeadersFrame(h ControlFrameHeader, frame *HeadersFrame) error {
|
||||||
|
frame.CFHeader = h
|
||||||
|
var err error
|
||||||
|
if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
reader := f.r
|
||||||
|
if !f.headerCompressionDisabled {
|
||||||
|
err := f.uncorkHeaderDecompressor(int64(h.length - 4))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
reader = f.headerDecompressor
|
||||||
|
}
|
||||||
|
frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId)
|
||||||
|
if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) {
|
||||||
|
err = &Error{WrongCompressedPayloadSize, 0}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var invalidHeaders map[string]bool
|
||||||
|
if frame.StreamId%2 == 0 {
|
||||||
|
invalidHeaders = invalidReqHeaders
|
||||||
|
} else {
|
||||||
|
invalidHeaders = invalidRespHeaders
|
||||||
|
}
|
||||||
|
for h := range frame.Headers {
|
||||||
|
if invalidHeaders[h] {
|
||||||
|
return &Error{InvalidHeaderPresent, frame.StreamId}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if frame.StreamId == 0 {
|
||||||
|
return &Error{ZeroStreamId, 0}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Framer) parseDataFrame(streamId StreamId) (*DataFrame, error) {
|
||||||
|
var length uint32
|
||||||
|
if err := binary.Read(f.r, binary.BigEndian, &length); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var frame DataFrame
|
||||||
|
frame.StreamId = streamId
|
||||||
|
frame.Flags = DataFlags(length >> 24)
|
||||||
|
length &= 0xffffff
|
||||||
|
frame.Data = make([]byte, length)
|
||||||
|
if _, err := io.ReadFull(f.r, frame.Data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if frame.StreamId == 0 {
|
||||||
|
return nil, &Error{ZeroStreamId, 0}
|
||||||
|
}
|
||||||
|
return &frame, nil
|
||||||
|
}
|
644
Godeps/_workspace/src/code.google.com/p/go.net/spdy/spdy_test.go
generated
vendored
Normal file
644
Godeps/_workspace/src/code.google.com/p/go.net/spdy/spdy_test.go
generated
vendored
Normal file
@@ -0,0 +1,644 @@
|
|||||||
|
// Copyright 2011 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 spdy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/zlib"
|
||||||
|
"encoding/base64"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var HeadersFixture = http.Header{
|
||||||
|
"Url": []string{"http://www.google.com/"},
|
||||||
|
"Method": []string{"get"},
|
||||||
|
"Version": []string{"http/1.1"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHeaderParsing(t *testing.T) {
|
||||||
|
var headerValueBlockBuf bytes.Buffer
|
||||||
|
writeHeaderValueBlock(&headerValueBlockBuf, HeadersFixture)
|
||||||
|
const bogusStreamId = 1
|
||||||
|
newHeaders, err := parseHeaderValueBlock(&headerValueBlockBuf, bogusStreamId)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("parseHeaderValueBlock:", err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(HeadersFixture, newHeaders) {
|
||||||
|
t.Fatal("got: ", newHeaders, "\nwant: ", HeadersFixture)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateParseSynStreamFrameCompressionDisable(t *testing.T) {
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
// Fixture framer for no compression test.
|
||||||
|
framer := &Framer{
|
||||||
|
headerCompressionDisabled: true,
|
||||||
|
w: buffer,
|
||||||
|
headerBuf: new(bytes.Buffer),
|
||||||
|
r: buffer,
|
||||||
|
}
|
||||||
|
synStreamFrame := SynStreamFrame{
|
||||||
|
CFHeader: ControlFrameHeader{
|
||||||
|
version: Version,
|
||||||
|
frameType: TypeSynStream,
|
||||||
|
},
|
||||||
|
StreamId: 2,
|
||||||
|
Headers: HeadersFixture,
|
||||||
|
}
|
||||||
|
if err := framer.WriteFrame(&synStreamFrame); err != nil {
|
||||||
|
t.Fatal("WriteFrame without compression:", err)
|
||||||
|
}
|
||||||
|
frame, err := framer.ReadFrame()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("ReadFrame without compression:", err)
|
||||||
|
}
|
||||||
|
parsedSynStreamFrame, ok := frame.(*SynStreamFrame)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Parsed incorrect frame type:", frame)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(synStreamFrame, *parsedSynStreamFrame) {
|
||||||
|
t.Fatal("got: ", *parsedSynStreamFrame, "\nwant: ", synStreamFrame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateParseSynStreamFrameCompressionEnable(t *testing.T) {
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
framer, err := NewFramer(buffer, buffer)
|
||||||
|
synStreamFrame := SynStreamFrame{
|
||||||
|
CFHeader: ControlFrameHeader{
|
||||||
|
version: Version,
|
||||||
|
frameType: TypeSynStream,
|
||||||
|
},
|
||||||
|
StreamId: 2,
|
||||||
|
Headers: HeadersFixture,
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Failed to create new framer:", err)
|
||||||
|
}
|
||||||
|
if err := framer.WriteFrame(&synStreamFrame); err != nil {
|
||||||
|
t.Fatal("WriteFrame with compression:", err)
|
||||||
|
}
|
||||||
|
frame, err := framer.ReadFrame()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("ReadFrame with compression:", err)
|
||||||
|
}
|
||||||
|
parsedSynStreamFrame, ok := frame.(*SynStreamFrame)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Parsed incorrect frame type:", frame)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(synStreamFrame, *parsedSynStreamFrame) {
|
||||||
|
t.Fatal("got: ", *parsedSynStreamFrame, "\nwant: ", synStreamFrame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateParseSynReplyFrameCompressionDisable(t *testing.T) {
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
framer := &Framer{
|
||||||
|
headerCompressionDisabled: true,
|
||||||
|
w: buffer,
|
||||||
|
headerBuf: new(bytes.Buffer),
|
||||||
|
r: buffer,
|
||||||
|
}
|
||||||
|
synReplyFrame := SynReplyFrame{
|
||||||
|
CFHeader: ControlFrameHeader{
|
||||||
|
version: Version,
|
||||||
|
frameType: TypeSynReply,
|
||||||
|
},
|
||||||
|
StreamId: 2,
|
||||||
|
Headers: HeadersFixture,
|
||||||
|
}
|
||||||
|
if err := framer.WriteFrame(&synReplyFrame); err != nil {
|
||||||
|
t.Fatal("WriteFrame without compression:", err)
|
||||||
|
}
|
||||||
|
frame, err := framer.ReadFrame()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("ReadFrame without compression:", err)
|
||||||
|
}
|
||||||
|
parsedSynReplyFrame, ok := frame.(*SynReplyFrame)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Parsed incorrect frame type:", frame)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(synReplyFrame, *parsedSynReplyFrame) {
|
||||||
|
t.Fatal("got: ", *parsedSynReplyFrame, "\nwant: ", synReplyFrame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateParseSynReplyFrameCompressionEnable(t *testing.T) {
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
framer, err := NewFramer(buffer, buffer)
|
||||||
|
synReplyFrame := SynReplyFrame{
|
||||||
|
CFHeader: ControlFrameHeader{
|
||||||
|
version: Version,
|
||||||
|
frameType: TypeSynReply,
|
||||||
|
},
|
||||||
|
StreamId: 2,
|
||||||
|
Headers: HeadersFixture,
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Failed to create new framer:", err)
|
||||||
|
}
|
||||||
|
if err := framer.WriteFrame(&synReplyFrame); err != nil {
|
||||||
|
t.Fatal("WriteFrame with compression:", err)
|
||||||
|
}
|
||||||
|
frame, err := framer.ReadFrame()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("ReadFrame with compression:", err)
|
||||||
|
}
|
||||||
|
parsedSynReplyFrame, ok := frame.(*SynReplyFrame)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Parsed incorrect frame type:", frame)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(synReplyFrame, *parsedSynReplyFrame) {
|
||||||
|
t.Fatal("got: ", *parsedSynReplyFrame, "\nwant: ", synReplyFrame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateParseRstStream(t *testing.T) {
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
framer, err := NewFramer(buffer, buffer)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Failed to create new framer:", err)
|
||||||
|
}
|
||||||
|
rstStreamFrame := RstStreamFrame{
|
||||||
|
CFHeader: ControlFrameHeader{
|
||||||
|
version: Version,
|
||||||
|
frameType: TypeRstStream,
|
||||||
|
},
|
||||||
|
StreamId: 1,
|
||||||
|
Status: InvalidStream,
|
||||||
|
}
|
||||||
|
if err := framer.WriteFrame(&rstStreamFrame); err != nil {
|
||||||
|
t.Fatal("WriteFrame:", err)
|
||||||
|
}
|
||||||
|
frame, err := framer.ReadFrame()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("ReadFrame:", err)
|
||||||
|
}
|
||||||
|
parsedRstStreamFrame, ok := frame.(*RstStreamFrame)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Parsed incorrect frame type:", frame)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(rstStreamFrame, *parsedRstStreamFrame) {
|
||||||
|
t.Fatal("got: ", *parsedRstStreamFrame, "\nwant: ", rstStreamFrame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateParseSettings(t *testing.T) {
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
framer, err := NewFramer(buffer, buffer)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Failed to create new framer:", err)
|
||||||
|
}
|
||||||
|
settingsFrame := SettingsFrame{
|
||||||
|
CFHeader: ControlFrameHeader{
|
||||||
|
version: Version,
|
||||||
|
frameType: TypeSettings,
|
||||||
|
},
|
||||||
|
FlagIdValues: []SettingsFlagIdValue{
|
||||||
|
{FlagSettingsPersistValue, SettingsCurrentCwnd, 10},
|
||||||
|
{FlagSettingsPersisted, SettingsUploadBandwidth, 1},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err := framer.WriteFrame(&settingsFrame); err != nil {
|
||||||
|
t.Fatal("WriteFrame:", err)
|
||||||
|
}
|
||||||
|
frame, err := framer.ReadFrame()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("ReadFrame:", err)
|
||||||
|
}
|
||||||
|
parsedSettingsFrame, ok := frame.(*SettingsFrame)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Parsed incorrect frame type:", frame)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(settingsFrame, *parsedSettingsFrame) {
|
||||||
|
t.Fatal("got: ", *parsedSettingsFrame, "\nwant: ", settingsFrame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateParsePing(t *testing.T) {
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
framer, err := NewFramer(buffer, buffer)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Failed to create new framer:", err)
|
||||||
|
}
|
||||||
|
pingFrame := PingFrame{
|
||||||
|
CFHeader: ControlFrameHeader{
|
||||||
|
version: Version,
|
||||||
|
frameType: TypePing,
|
||||||
|
},
|
||||||
|
Id: 31337,
|
||||||
|
}
|
||||||
|
if err := framer.WriteFrame(&pingFrame); err != nil {
|
||||||
|
t.Fatal("WriteFrame:", err)
|
||||||
|
}
|
||||||
|
if pingFrame.CFHeader.Flags != 0 {
|
||||||
|
t.Fatal("Incorrect frame type:", pingFrame)
|
||||||
|
}
|
||||||
|
frame, err := framer.ReadFrame()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("ReadFrame:", err)
|
||||||
|
}
|
||||||
|
parsedPingFrame, ok := frame.(*PingFrame)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Parsed incorrect frame type:", frame)
|
||||||
|
}
|
||||||
|
if parsedPingFrame.CFHeader.Flags != 0 {
|
||||||
|
t.Fatal("Parsed incorrect frame type:", parsedPingFrame)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(pingFrame, *parsedPingFrame) {
|
||||||
|
t.Fatal("got: ", *parsedPingFrame, "\nwant: ", pingFrame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateParseGoAway(t *testing.T) {
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
framer, err := NewFramer(buffer, buffer)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Failed to create new framer:", err)
|
||||||
|
}
|
||||||
|
goAwayFrame := GoAwayFrame{
|
||||||
|
CFHeader: ControlFrameHeader{
|
||||||
|
version: Version,
|
||||||
|
frameType: TypeGoAway,
|
||||||
|
},
|
||||||
|
LastGoodStreamId: 31337,
|
||||||
|
Status: 1,
|
||||||
|
}
|
||||||
|
if err := framer.WriteFrame(&goAwayFrame); err != nil {
|
||||||
|
t.Fatal("WriteFrame:", err)
|
||||||
|
}
|
||||||
|
if goAwayFrame.CFHeader.Flags != 0 {
|
||||||
|
t.Fatal("Incorrect frame type:", goAwayFrame)
|
||||||
|
}
|
||||||
|
if goAwayFrame.CFHeader.length != 8 {
|
||||||
|
t.Fatal("Incorrect frame type:", goAwayFrame)
|
||||||
|
}
|
||||||
|
frame, err := framer.ReadFrame()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("ReadFrame:", err)
|
||||||
|
}
|
||||||
|
parsedGoAwayFrame, ok := frame.(*GoAwayFrame)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Parsed incorrect frame type:", frame)
|
||||||
|
}
|
||||||
|
if parsedGoAwayFrame.CFHeader.Flags != 0 {
|
||||||
|
t.Fatal("Incorrect frame type:", parsedGoAwayFrame)
|
||||||
|
}
|
||||||
|
if parsedGoAwayFrame.CFHeader.length != 8 {
|
||||||
|
t.Fatal("Incorrect frame type:", parsedGoAwayFrame)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(goAwayFrame, *parsedGoAwayFrame) {
|
||||||
|
t.Fatal("got: ", *parsedGoAwayFrame, "\nwant: ", goAwayFrame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateParseHeadersFrame(t *testing.T) {
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
framer := &Framer{
|
||||||
|
headerCompressionDisabled: true,
|
||||||
|
w: buffer,
|
||||||
|
headerBuf: new(bytes.Buffer),
|
||||||
|
r: buffer,
|
||||||
|
}
|
||||||
|
headersFrame := HeadersFrame{
|
||||||
|
CFHeader: ControlFrameHeader{
|
||||||
|
version: Version,
|
||||||
|
frameType: TypeHeaders,
|
||||||
|
},
|
||||||
|
StreamId: 2,
|
||||||
|
}
|
||||||
|
headersFrame.Headers = HeadersFixture
|
||||||
|
if err := framer.WriteFrame(&headersFrame); err != nil {
|
||||||
|
t.Fatal("WriteFrame without compression:", err)
|
||||||
|
}
|
||||||
|
frame, err := framer.ReadFrame()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("ReadFrame without compression:", err)
|
||||||
|
}
|
||||||
|
parsedHeadersFrame, ok := frame.(*HeadersFrame)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Parsed incorrect frame type:", frame)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(headersFrame, *parsedHeadersFrame) {
|
||||||
|
t.Fatal("got: ", *parsedHeadersFrame, "\nwant: ", headersFrame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateParseHeadersFrameCompressionEnable(t *testing.T) {
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
headersFrame := HeadersFrame{
|
||||||
|
CFHeader: ControlFrameHeader{
|
||||||
|
version: Version,
|
||||||
|
frameType: TypeHeaders,
|
||||||
|
},
|
||||||
|
StreamId: 2,
|
||||||
|
}
|
||||||
|
headersFrame.Headers = HeadersFixture
|
||||||
|
|
||||||
|
framer, err := NewFramer(buffer, buffer)
|
||||||
|
if err := framer.WriteFrame(&headersFrame); err != nil {
|
||||||
|
t.Fatal("WriteFrame with compression:", err)
|
||||||
|
}
|
||||||
|
frame, err := framer.ReadFrame()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("ReadFrame with compression:", err)
|
||||||
|
}
|
||||||
|
parsedHeadersFrame, ok := frame.(*HeadersFrame)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Parsed incorrect frame type:", frame)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(headersFrame, *parsedHeadersFrame) {
|
||||||
|
t.Fatal("got: ", *parsedHeadersFrame, "\nwant: ", headersFrame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateParseWindowUpdateFrame(t *testing.T) {
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
framer, err := NewFramer(buffer, buffer)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Failed to create new framer:", err)
|
||||||
|
}
|
||||||
|
windowUpdateFrame := WindowUpdateFrame{
|
||||||
|
CFHeader: ControlFrameHeader{
|
||||||
|
version: Version,
|
||||||
|
frameType: TypeWindowUpdate,
|
||||||
|
},
|
||||||
|
StreamId: 31337,
|
||||||
|
DeltaWindowSize: 1,
|
||||||
|
}
|
||||||
|
if err := framer.WriteFrame(&windowUpdateFrame); err != nil {
|
||||||
|
t.Fatal("WriteFrame:", err)
|
||||||
|
}
|
||||||
|
if windowUpdateFrame.CFHeader.Flags != 0 {
|
||||||
|
t.Fatal("Incorrect frame type:", windowUpdateFrame)
|
||||||
|
}
|
||||||
|
if windowUpdateFrame.CFHeader.length != 8 {
|
||||||
|
t.Fatal("Incorrect frame type:", windowUpdateFrame)
|
||||||
|
}
|
||||||
|
frame, err := framer.ReadFrame()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("ReadFrame:", err)
|
||||||
|
}
|
||||||
|
parsedWindowUpdateFrame, ok := frame.(*WindowUpdateFrame)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Parsed incorrect frame type:", frame)
|
||||||
|
}
|
||||||
|
if parsedWindowUpdateFrame.CFHeader.Flags != 0 {
|
||||||
|
t.Fatal("Incorrect frame type:", parsedWindowUpdateFrame)
|
||||||
|
}
|
||||||
|
if parsedWindowUpdateFrame.CFHeader.length != 8 {
|
||||||
|
t.Fatal("Incorrect frame type:", parsedWindowUpdateFrame)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(windowUpdateFrame, *parsedWindowUpdateFrame) {
|
||||||
|
t.Fatal("got: ", *parsedWindowUpdateFrame, "\nwant: ", windowUpdateFrame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateParseDataFrame(t *testing.T) {
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
framer, err := NewFramer(buffer, buffer)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Failed to create new framer:", err)
|
||||||
|
}
|
||||||
|
dataFrame := DataFrame{
|
||||||
|
StreamId: 1,
|
||||||
|
Data: []byte{'h', 'e', 'l', 'l', 'o'},
|
||||||
|
}
|
||||||
|
if err := framer.WriteFrame(&dataFrame); err != nil {
|
||||||
|
t.Fatal("WriteFrame:", err)
|
||||||
|
}
|
||||||
|
frame, err := framer.ReadFrame()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("ReadFrame:", err)
|
||||||
|
}
|
||||||
|
parsedDataFrame, ok := frame.(*DataFrame)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Parsed incorrect frame type:", frame)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(dataFrame, *parsedDataFrame) {
|
||||||
|
t.Fatal("got: ", *parsedDataFrame, "\nwant: ", dataFrame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCompressionContextAcrossFrames(t *testing.T) {
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
framer, err := NewFramer(buffer, buffer)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Failed to create new framer:", err)
|
||||||
|
}
|
||||||
|
headersFrame := HeadersFrame{
|
||||||
|
CFHeader: ControlFrameHeader{
|
||||||
|
version: Version,
|
||||||
|
frameType: TypeHeaders,
|
||||||
|
},
|
||||||
|
StreamId: 2,
|
||||||
|
Headers: HeadersFixture,
|
||||||
|
}
|
||||||
|
if err := framer.WriteFrame(&headersFrame); err != nil {
|
||||||
|
t.Fatal("WriteFrame (HEADERS):", err)
|
||||||
|
}
|
||||||
|
synStreamFrame := SynStreamFrame{
|
||||||
|
ControlFrameHeader{
|
||||||
|
Version,
|
||||||
|
TypeSynStream,
|
||||||
|
0, // Flags
|
||||||
|
0, // length
|
||||||
|
},
|
||||||
|
2, // StreamId
|
||||||
|
0, // AssociatedTOStreamID
|
||||||
|
0, // Priority
|
||||||
|
1, // Slot
|
||||||
|
nil, // Headers
|
||||||
|
}
|
||||||
|
synStreamFrame.Headers = HeadersFixture
|
||||||
|
|
||||||
|
if err := framer.WriteFrame(&synStreamFrame); err != nil {
|
||||||
|
t.Fatal("WriteFrame (SYN_STREAM):", err)
|
||||||
|
}
|
||||||
|
frame, err := framer.ReadFrame()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("ReadFrame (HEADERS):", err, buffer.Bytes())
|
||||||
|
}
|
||||||
|
parsedHeadersFrame, ok := frame.(*HeadersFrame)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("expected HeadersFrame; got %T %v", frame, frame)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(headersFrame, *parsedHeadersFrame) {
|
||||||
|
t.Fatal("got: ", *parsedHeadersFrame, "\nwant: ", headersFrame)
|
||||||
|
}
|
||||||
|
frame, err = framer.ReadFrame()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("ReadFrame (SYN_STREAM):", err, buffer.Bytes())
|
||||||
|
}
|
||||||
|
parsedSynStreamFrame, ok := frame.(*SynStreamFrame)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("expected SynStreamFrame; got %T %v", frame, frame)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(synStreamFrame, *parsedSynStreamFrame) {
|
||||||
|
t.Fatal("got: ", *parsedSynStreamFrame, "\nwant: ", synStreamFrame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMultipleSPDYFrames(t *testing.T) {
|
||||||
|
// Initialize the framers.
|
||||||
|
pr1, pw1 := io.Pipe()
|
||||||
|
pr2, pw2 := io.Pipe()
|
||||||
|
writer, err := NewFramer(pw1, pr2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Failed to create writer:", err)
|
||||||
|
}
|
||||||
|
reader, err := NewFramer(pw2, pr1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Failed to create reader:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up the frames we're actually transferring.
|
||||||
|
headersFrame := HeadersFrame{
|
||||||
|
CFHeader: ControlFrameHeader{
|
||||||
|
version: Version,
|
||||||
|
frameType: TypeHeaders,
|
||||||
|
},
|
||||||
|
StreamId: 2,
|
||||||
|
Headers: HeadersFixture,
|
||||||
|
}
|
||||||
|
synStreamFrame := SynStreamFrame{
|
||||||
|
CFHeader: ControlFrameHeader{
|
||||||
|
version: Version,
|
||||||
|
frameType: TypeSynStream,
|
||||||
|
},
|
||||||
|
StreamId: 2,
|
||||||
|
Headers: HeadersFixture,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the goroutines to write the frames.
|
||||||
|
go func() {
|
||||||
|
if err := writer.WriteFrame(&headersFrame); err != nil {
|
||||||
|
t.Fatal("WriteFrame (HEADERS): ", err)
|
||||||
|
}
|
||||||
|
if err := writer.WriteFrame(&synStreamFrame); err != nil {
|
||||||
|
t.Fatal("WriteFrame (SYN_STREAM): ", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Read the frames and verify they look as expected.
|
||||||
|
frame, err := reader.ReadFrame()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("ReadFrame (HEADERS): ", err)
|
||||||
|
}
|
||||||
|
parsedHeadersFrame, ok := frame.(*HeadersFrame)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Parsed incorrect frame type:", frame)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(headersFrame, *parsedHeadersFrame) {
|
||||||
|
t.Fatal("got: ", *parsedHeadersFrame, "\nwant: ", headersFrame)
|
||||||
|
}
|
||||||
|
frame, err = reader.ReadFrame()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("ReadFrame (SYN_STREAM):", err)
|
||||||
|
}
|
||||||
|
parsedSynStreamFrame, ok := frame.(*SynStreamFrame)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Parsed incorrect frame type.")
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(synStreamFrame, *parsedSynStreamFrame) {
|
||||||
|
t.Fatal("got: ", *parsedSynStreamFrame, "\nwant: ", synStreamFrame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadMalformedZlibHeader(t *testing.T) {
|
||||||
|
// These were constructed by corrupting the first byte of the zlib
|
||||||
|
// header after writing.
|
||||||
|
malformedStructs := map[string]string{
|
||||||
|
"SynStreamFrame": "gAIAAQAAABgAAAACAAAAAAAAF/nfolGyYmAAAAAA//8=",
|
||||||
|
"SynReplyFrame": "gAIAAgAAABQAAAACAAAX+d+iUbJiYAAAAAD//w==",
|
||||||
|
"HeadersFrame": "gAIACAAAABQAAAACAAAX+d+iUbJiYAAAAAD//w==",
|
||||||
|
}
|
||||||
|
for name, bad := range malformedStructs {
|
||||||
|
b, err := base64.StdEncoding.DecodeString(bad)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unable to decode base64 encoded frame %s: %v", name, err)
|
||||||
|
}
|
||||||
|
buf := bytes.NewBuffer(b)
|
||||||
|
reader, err := NewFramer(buf, buf)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewFramer: %v", err)
|
||||||
|
}
|
||||||
|
_, err = reader.ReadFrame()
|
||||||
|
if err != zlib.ErrHeader {
|
||||||
|
t.Errorf("Frame %s, expected: %#v, actual: %#v", name, zlib.ErrHeader, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: these tests are too weak for updating SPDY spec. Fix me.
|
||||||
|
|
||||||
|
type zeroStream struct {
|
||||||
|
frame Frame
|
||||||
|
encoded string
|
||||||
|
}
|
||||||
|
|
||||||
|
var streamIdZeroFrames = map[string]zeroStream{
|
||||||
|
"SynStreamFrame": {
|
||||||
|
&SynStreamFrame{StreamId: 0},
|
||||||
|
"gAIAAQAAABgAAAAAAAAAAAAAePnfolGyYmAAAAAA//8=",
|
||||||
|
},
|
||||||
|
"SynReplyFrame": {
|
||||||
|
&SynReplyFrame{StreamId: 0},
|
||||||
|
"gAIAAgAAABQAAAAAAAB4+d+iUbJiYAAAAAD//w==",
|
||||||
|
},
|
||||||
|
"RstStreamFrame": {
|
||||||
|
&RstStreamFrame{StreamId: 0},
|
||||||
|
"gAIAAwAAAAgAAAAAAAAAAA==",
|
||||||
|
},
|
||||||
|
"HeadersFrame": {
|
||||||
|
&HeadersFrame{StreamId: 0},
|
||||||
|
"gAIACAAAABQAAAAAAAB4+d+iUbJiYAAAAAD//w==",
|
||||||
|
},
|
||||||
|
"DataFrame": {
|
||||||
|
&DataFrame{StreamId: 0},
|
||||||
|
"AAAAAAAAAAA=",
|
||||||
|
},
|
||||||
|
"PingFrame": {
|
||||||
|
&PingFrame{Id: 0},
|
||||||
|
"gAIABgAAAAQAAAAA",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoZeroStreamId(t *testing.T) {
|
||||||
|
t.Log("skipping") // TODO: update to work with SPDY3
|
||||||
|
return
|
||||||
|
|
||||||
|
for name, f := range streamIdZeroFrames {
|
||||||
|
b, err := base64.StdEncoding.DecodeString(f.encoded)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unable to decode base64 encoded frame %s: %v", f, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
framer, err := NewFramer(ioutil.Discard, bytes.NewReader(b))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewFramer: %v", err)
|
||||||
|
}
|
||||||
|
err = framer.WriteFrame(f.frame)
|
||||||
|
checkZeroStreamId(t, name, "WriteFrame", err)
|
||||||
|
|
||||||
|
_, err = framer.ReadFrame()
|
||||||
|
checkZeroStreamId(t, name, "ReadFrame", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkZeroStreamId(t *testing.T, frame string, method string, err error) {
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("%s ZeroStreamId, no error on %s", method, frame)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
eerr, ok := err.(*Error)
|
||||||
|
if !ok || eerr.Err != ZeroStreamId {
|
||||||
|
t.Errorf("%s ZeroStreamId, incorrect error %#v, frame %s", method, eerr, frame)
|
||||||
|
}
|
||||||
|
}
|
275
Godeps/_workspace/src/code.google.com/p/go.net/spdy/types.go
generated
vendored
Normal file
275
Godeps/_workspace/src/code.google.com/p/go.net/spdy/types.go
generated
vendored
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
// Copyright 2011 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 spdy implements the SPDY protocol (currently SPDY/3), described in
|
||||||
|
// http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3.
|
||||||
|
package spdy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/zlib"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Version is the protocol version number that this package implements.
|
||||||
|
const Version = 3
|
||||||
|
|
||||||
|
// ControlFrameType stores the type field in a control frame header.
|
||||||
|
type ControlFrameType uint16
|
||||||
|
|
||||||
|
const (
|
||||||
|
TypeSynStream ControlFrameType = 0x0001
|
||||||
|
TypeSynReply = 0x0002
|
||||||
|
TypeRstStream = 0x0003
|
||||||
|
TypeSettings = 0x0004
|
||||||
|
TypePing = 0x0006
|
||||||
|
TypeGoAway = 0x0007
|
||||||
|
TypeHeaders = 0x0008
|
||||||
|
TypeWindowUpdate = 0x0009
|
||||||
|
)
|
||||||
|
|
||||||
|
// ControlFlags are the flags that can be set on a control frame.
|
||||||
|
type ControlFlags uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
ControlFlagFin ControlFlags = 0x01
|
||||||
|
ControlFlagUnidirectional = 0x02
|
||||||
|
ControlFlagSettingsClearSettings = 0x01
|
||||||
|
)
|
||||||
|
|
||||||
|
// DataFlags are the flags that can be set on a data frame.
|
||||||
|
type DataFlags uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
DataFlagFin DataFlags = 0x01
|
||||||
|
)
|
||||||
|
|
||||||
|
// MaxDataLength is the maximum number of bytes that can be stored in one frame.
|
||||||
|
const MaxDataLength = 1<<24 - 1
|
||||||
|
|
||||||
|
// headerValueSepator separates multiple header values.
|
||||||
|
const headerValueSeparator = "\x00"
|
||||||
|
|
||||||
|
// Frame is a single SPDY frame in its unpacked in-memory representation. Use
|
||||||
|
// Framer to read and write it.
|
||||||
|
type Frame interface {
|
||||||
|
write(f *Framer) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ControlFrameHeader contains all the fields in a control frame header,
|
||||||
|
// in its unpacked in-memory representation.
|
||||||
|
type ControlFrameHeader struct {
|
||||||
|
// Note, high bit is the "Control" bit.
|
||||||
|
version uint16 // spdy version number
|
||||||
|
frameType ControlFrameType
|
||||||
|
Flags ControlFlags
|
||||||
|
length uint32 // length of data field
|
||||||
|
}
|
||||||
|
|
||||||
|
type controlFrame interface {
|
||||||
|
Frame
|
||||||
|
read(h ControlFrameHeader, f *Framer) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// StreamId represents a 31-bit value identifying the stream.
|
||||||
|
type StreamId uint32
|
||||||
|
|
||||||
|
// SynStreamFrame is the unpacked, in-memory representation of a SYN_STREAM
|
||||||
|
// frame.
|
||||||
|
type SynStreamFrame struct {
|
||||||
|
CFHeader ControlFrameHeader
|
||||||
|
StreamId StreamId
|
||||||
|
AssociatedToStreamId StreamId // stream id for a stream which this stream is associated to
|
||||||
|
Priority uint8 // priority of this frame (3-bit)
|
||||||
|
Slot uint8 // index in the server's credential vector of the client certificate
|
||||||
|
Headers http.Header
|
||||||
|
}
|
||||||
|
|
||||||
|
// SynReplyFrame is the unpacked, in-memory representation of a SYN_REPLY frame.
|
||||||
|
type SynReplyFrame struct {
|
||||||
|
CFHeader ControlFrameHeader
|
||||||
|
StreamId StreamId
|
||||||
|
Headers http.Header
|
||||||
|
}
|
||||||
|
|
||||||
|
// RstStreamStatus represents the status that led to a RST_STREAM.
|
||||||
|
type RstStreamStatus uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
ProtocolError RstStreamStatus = iota + 1
|
||||||
|
InvalidStream
|
||||||
|
RefusedStream
|
||||||
|
UnsupportedVersion
|
||||||
|
Cancel
|
||||||
|
InternalError
|
||||||
|
FlowControlError
|
||||||
|
StreamInUse
|
||||||
|
StreamAlreadyClosed
|
||||||
|
InvalidCredentials
|
||||||
|
FrameTooLarge
|
||||||
|
)
|
||||||
|
|
||||||
|
// RstStreamFrame is the unpacked, in-memory representation of a RST_STREAM
|
||||||
|
// frame.
|
||||||
|
type RstStreamFrame struct {
|
||||||
|
CFHeader ControlFrameHeader
|
||||||
|
StreamId StreamId
|
||||||
|
Status RstStreamStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
// SettingsFlag represents a flag in a SETTINGS frame.
|
||||||
|
type SettingsFlag uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
FlagSettingsPersistValue SettingsFlag = 0x1
|
||||||
|
FlagSettingsPersisted = 0x2
|
||||||
|
)
|
||||||
|
|
||||||
|
// SettingsFlag represents the id of an id/value pair in a SETTINGS frame.
|
||||||
|
type SettingsId uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
SettingsUploadBandwidth SettingsId = iota + 1
|
||||||
|
SettingsDownloadBandwidth
|
||||||
|
SettingsRoundTripTime
|
||||||
|
SettingsMaxConcurrentStreams
|
||||||
|
SettingsCurrentCwnd
|
||||||
|
SettingsDownloadRetransRate
|
||||||
|
SettingsInitialWindowSize
|
||||||
|
SettingsClientCretificateVectorSize
|
||||||
|
)
|
||||||
|
|
||||||
|
// SettingsFlagIdValue is the unpacked, in-memory representation of the
|
||||||
|
// combined flag/id/value for a setting in a SETTINGS frame.
|
||||||
|
type SettingsFlagIdValue struct {
|
||||||
|
Flag SettingsFlag
|
||||||
|
Id SettingsId
|
||||||
|
Value uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// SettingsFrame is the unpacked, in-memory representation of a SPDY
|
||||||
|
// SETTINGS frame.
|
||||||
|
type SettingsFrame struct {
|
||||||
|
CFHeader ControlFrameHeader
|
||||||
|
FlagIdValues []SettingsFlagIdValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// PingFrame is the unpacked, in-memory representation of a PING frame.
|
||||||
|
type PingFrame struct {
|
||||||
|
CFHeader ControlFrameHeader
|
||||||
|
Id uint32 // unique id for this ping, from server is even, from client is odd.
|
||||||
|
}
|
||||||
|
|
||||||
|
// GoAwayStatus represents the status in a GoAwayFrame.
|
||||||
|
type GoAwayStatus uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
GoAwayOK GoAwayStatus = iota
|
||||||
|
GoAwayProtocolError
|
||||||
|
GoAwayInternalError
|
||||||
|
)
|
||||||
|
|
||||||
|
// GoAwayFrame is the unpacked, in-memory representation of a GOAWAY frame.
|
||||||
|
type GoAwayFrame struct {
|
||||||
|
CFHeader ControlFrameHeader
|
||||||
|
LastGoodStreamId StreamId // last stream id which was accepted by sender
|
||||||
|
Status GoAwayStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
// HeadersFrame is the unpacked, in-memory representation of a HEADERS frame.
|
||||||
|
type HeadersFrame struct {
|
||||||
|
CFHeader ControlFrameHeader
|
||||||
|
StreamId StreamId
|
||||||
|
Headers http.Header
|
||||||
|
}
|
||||||
|
|
||||||
|
// WindowUpdateFrame is the unpacked, in-memory representation of a
|
||||||
|
// WINDOW_UPDATE frame.
|
||||||
|
type WindowUpdateFrame struct {
|
||||||
|
CFHeader ControlFrameHeader
|
||||||
|
StreamId StreamId
|
||||||
|
DeltaWindowSize uint32 // additional number of bytes to existing window size
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Implement credential frame and related methods.
|
||||||
|
|
||||||
|
// DataFrame is the unpacked, in-memory representation of a DATA frame.
|
||||||
|
type DataFrame struct {
|
||||||
|
// Note, high bit is the "Control" bit. Should be 0 for data frames.
|
||||||
|
StreamId StreamId
|
||||||
|
Flags DataFlags
|
||||||
|
Data []byte // payload data of this frame
|
||||||
|
}
|
||||||
|
|
||||||
|
// A SPDY specific error.
|
||||||
|
type ErrorCode string
|
||||||
|
|
||||||
|
const (
|
||||||
|
UnlowercasedHeaderName ErrorCode = "header was not lowercased"
|
||||||
|
DuplicateHeaders = "multiple headers with same name"
|
||||||
|
WrongCompressedPayloadSize = "compressed payload size was incorrect"
|
||||||
|
UnknownFrameType = "unknown frame type"
|
||||||
|
InvalidControlFrame = "invalid control frame"
|
||||||
|
InvalidDataFrame = "invalid data frame"
|
||||||
|
InvalidHeaderPresent = "frame contained invalid header"
|
||||||
|
ZeroStreamId = "stream id zero is disallowed"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Error contains both the type of error and additional values. StreamId is 0
|
||||||
|
// if Error is not associated with a stream.
|
||||||
|
type Error struct {
|
||||||
|
Err ErrorCode
|
||||||
|
StreamId StreamId
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Error() string {
|
||||||
|
return string(e.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var invalidReqHeaders = map[string]bool{
|
||||||
|
"Connection": true,
|
||||||
|
"Host": true,
|
||||||
|
"Keep-Alive": true,
|
||||||
|
"Proxy-Connection": true,
|
||||||
|
"Transfer-Encoding": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var invalidRespHeaders = map[string]bool{
|
||||||
|
"Connection": true,
|
||||||
|
"Keep-Alive": true,
|
||||||
|
"Proxy-Connection": true,
|
||||||
|
"Transfer-Encoding": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Framer handles serializing/deserializing SPDY frames, including compressing/
|
||||||
|
// decompressing payloads.
|
||||||
|
type Framer struct {
|
||||||
|
headerCompressionDisabled bool
|
||||||
|
w io.Writer
|
||||||
|
headerBuf *bytes.Buffer
|
||||||
|
headerCompressor *zlib.Writer
|
||||||
|
r io.Reader
|
||||||
|
headerReader io.LimitedReader
|
||||||
|
headerDecompressor io.ReadCloser
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFramer allocates a new Framer for a given SPDY connection, represented by
|
||||||
|
// a io.Writer and io.Reader. Note that Framer will read and write individual fields
|
||||||
|
// from/to the Reader and Writer, so the caller should pass in an appropriately
|
||||||
|
// buffered implementation to optimize performance.
|
||||||
|
func NewFramer(w io.Writer, r io.Reader) (*Framer, error) {
|
||||||
|
compressBuf := new(bytes.Buffer)
|
||||||
|
compressor, err := zlib.NewWriterLevelDict(compressBuf, zlib.BestCompression, []byte(headerDictionary))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
framer := &Framer{
|
||||||
|
w: w,
|
||||||
|
headerBuf: compressBuf,
|
||||||
|
headerCompressor: compressor,
|
||||||
|
r: r,
|
||||||
|
}
|
||||||
|
return framer, nil
|
||||||
|
}
|
318
Godeps/_workspace/src/code.google.com/p/go.net/spdy/write.go
generated
vendored
Normal file
318
Godeps/_workspace/src/code.google.com/p/go.net/spdy/write.go
generated
vendored
Normal file
@@ -0,0 +1,318 @@
|
|||||||
|
// Copyright 2011 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 spdy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (frame *SynStreamFrame) write(f *Framer) error {
|
||||||
|
return f.writeSynStreamFrame(frame)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (frame *SynReplyFrame) write(f *Framer) error {
|
||||||
|
return f.writeSynReplyFrame(frame)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (frame *RstStreamFrame) write(f *Framer) (err error) {
|
||||||
|
if frame.StreamId == 0 {
|
||||||
|
return &Error{ZeroStreamId, 0}
|
||||||
|
}
|
||||||
|
frame.CFHeader.version = Version
|
||||||
|
frame.CFHeader.frameType = TypeRstStream
|
||||||
|
frame.CFHeader.Flags = 0
|
||||||
|
frame.CFHeader.length = 8
|
||||||
|
|
||||||
|
// Serialize frame to Writer.
|
||||||
|
if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if frame.Status == 0 {
|
||||||
|
return &Error{InvalidControlFrame, frame.StreamId}
|
||||||
|
}
|
||||||
|
if err = binary.Write(f.w, binary.BigEndian, frame.Status); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (frame *SettingsFrame) write(f *Framer) (err error) {
|
||||||
|
frame.CFHeader.version = Version
|
||||||
|
frame.CFHeader.frameType = TypeSettings
|
||||||
|
frame.CFHeader.length = uint32(len(frame.FlagIdValues)*8 + 4)
|
||||||
|
|
||||||
|
// Serialize frame to Writer.
|
||||||
|
if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = binary.Write(f.w, binary.BigEndian, uint32(len(frame.FlagIdValues))); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, flagIdValue := range frame.FlagIdValues {
|
||||||
|
flagId := uint32(flagIdValue.Flag)<<24 | uint32(flagIdValue.Id)
|
||||||
|
if err = binary.Write(f.w, binary.BigEndian, flagId); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = binary.Write(f.w, binary.BigEndian, flagIdValue.Value); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (frame *PingFrame) write(f *Framer) (err error) {
|
||||||
|
if frame.Id == 0 {
|
||||||
|
return &Error{ZeroStreamId, 0}
|
||||||
|
}
|
||||||
|
frame.CFHeader.version = Version
|
||||||
|
frame.CFHeader.frameType = TypePing
|
||||||
|
frame.CFHeader.Flags = 0
|
||||||
|
frame.CFHeader.length = 4
|
||||||
|
|
||||||
|
// Serialize frame to Writer.
|
||||||
|
if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = binary.Write(f.w, binary.BigEndian, frame.Id); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (frame *GoAwayFrame) write(f *Framer) (err error) {
|
||||||
|
frame.CFHeader.version = Version
|
||||||
|
frame.CFHeader.frameType = TypeGoAway
|
||||||
|
frame.CFHeader.Flags = 0
|
||||||
|
frame.CFHeader.length = 8
|
||||||
|
|
||||||
|
// Serialize frame to Writer.
|
||||||
|
if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = binary.Write(f.w, binary.BigEndian, frame.LastGoodStreamId); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = binary.Write(f.w, binary.BigEndian, frame.Status); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (frame *HeadersFrame) write(f *Framer) error {
|
||||||
|
return f.writeHeadersFrame(frame)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (frame *WindowUpdateFrame) write(f *Framer) (err error) {
|
||||||
|
frame.CFHeader.version = Version
|
||||||
|
frame.CFHeader.frameType = TypeWindowUpdate
|
||||||
|
frame.CFHeader.Flags = 0
|
||||||
|
frame.CFHeader.length = 8
|
||||||
|
|
||||||
|
// Serialize frame to Writer.
|
||||||
|
if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = binary.Write(f.w, binary.BigEndian, frame.DeltaWindowSize); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (frame *DataFrame) write(f *Framer) error {
|
||||||
|
return f.writeDataFrame(frame)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteFrame writes a frame.
|
||||||
|
func (f *Framer) WriteFrame(frame Frame) error {
|
||||||
|
return frame.write(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeControlFrameHeader(w io.Writer, h ControlFrameHeader) error {
|
||||||
|
if err := binary.Write(w, binary.BigEndian, 0x8000|h.version); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := binary.Write(w, binary.BigEndian, h.frameType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
flagsAndLength := uint32(h.Flags)<<24 | h.length
|
||||||
|
if err := binary.Write(w, binary.BigEndian, flagsAndLength); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeHeaderValueBlock(w io.Writer, h http.Header) (n int, err error) {
|
||||||
|
n = 0
|
||||||
|
if err = binary.Write(w, binary.BigEndian, uint32(len(h))); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n += 2
|
||||||
|
for name, values := range h {
|
||||||
|
if err = binary.Write(w, binary.BigEndian, uint32(len(name))); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n += 2
|
||||||
|
name = strings.ToLower(name)
|
||||||
|
if _, err = io.WriteString(w, name); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n += len(name)
|
||||||
|
v := strings.Join(values, headerValueSeparator)
|
||||||
|
if err = binary.Write(w, binary.BigEndian, uint32(len(v))); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n += 2
|
||||||
|
if _, err = io.WriteString(w, v); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n += len(v)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Framer) writeSynStreamFrame(frame *SynStreamFrame) (err error) {
|
||||||
|
if frame.StreamId == 0 {
|
||||||
|
return &Error{ZeroStreamId, 0}
|
||||||
|
}
|
||||||
|
// Marshal the headers.
|
||||||
|
var writer io.Writer = f.headerBuf
|
||||||
|
if !f.headerCompressionDisabled {
|
||||||
|
writer = f.headerCompressor
|
||||||
|
}
|
||||||
|
if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !f.headerCompressionDisabled {
|
||||||
|
f.headerCompressor.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set ControlFrameHeader.
|
||||||
|
frame.CFHeader.version = Version
|
||||||
|
frame.CFHeader.frameType = TypeSynStream
|
||||||
|
frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 10)
|
||||||
|
|
||||||
|
// Serialize frame to Writer.
|
||||||
|
if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = binary.Write(f.w, binary.BigEndian, frame.AssociatedToStreamId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = binary.Write(f.w, binary.BigEndian, frame.Priority<<5); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = binary.Write(f.w, binary.BigEndian, frame.Slot); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f.headerBuf.Reset()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Framer) writeSynReplyFrame(frame *SynReplyFrame) (err error) {
|
||||||
|
if frame.StreamId == 0 {
|
||||||
|
return &Error{ZeroStreamId, 0}
|
||||||
|
}
|
||||||
|
// Marshal the headers.
|
||||||
|
var writer io.Writer = f.headerBuf
|
||||||
|
if !f.headerCompressionDisabled {
|
||||||
|
writer = f.headerCompressor
|
||||||
|
}
|
||||||
|
if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !f.headerCompressionDisabled {
|
||||||
|
f.headerCompressor.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set ControlFrameHeader.
|
||||||
|
frame.CFHeader.version = Version
|
||||||
|
frame.CFHeader.frameType = TypeSynReply
|
||||||
|
frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 4)
|
||||||
|
|
||||||
|
// Serialize frame to Writer.
|
||||||
|
if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
f.headerBuf.Reset()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Framer) writeHeadersFrame(frame *HeadersFrame) (err error) {
|
||||||
|
if frame.StreamId == 0 {
|
||||||
|
return &Error{ZeroStreamId, 0}
|
||||||
|
}
|
||||||
|
// Marshal the headers.
|
||||||
|
var writer io.Writer = f.headerBuf
|
||||||
|
if !f.headerCompressionDisabled {
|
||||||
|
writer = f.headerCompressor
|
||||||
|
}
|
||||||
|
if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !f.headerCompressionDisabled {
|
||||||
|
f.headerCompressor.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set ControlFrameHeader.
|
||||||
|
frame.CFHeader.version = Version
|
||||||
|
frame.CFHeader.frameType = TypeHeaders
|
||||||
|
frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 4)
|
||||||
|
|
||||||
|
// Serialize frame to Writer.
|
||||||
|
if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
f.headerBuf.Reset()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Framer) writeDataFrame(frame *DataFrame) (err error) {
|
||||||
|
if frame.StreamId == 0 {
|
||||||
|
return &Error{ZeroStreamId, 0}
|
||||||
|
}
|
||||||
|
if frame.StreamId&0x80000000 != 0 || len(frame.Data) > MaxDataLength {
|
||||||
|
return &Error{InvalidDataFrame, frame.StreamId}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize frame to Writer.
|
||||||
|
if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
flagsAndLength := uint32(frame.Flags)<<24 | uint32(len(frame.Data))
|
||||||
|
if err = binary.Write(f.w, binary.BigEndian, flagsAndLength); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err = f.w.Write(frame.Data); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
1
Godeps/_workspace/src/github.com/docker/docker/pkg/term/MAINTAINERS
generated
vendored
Normal file
1
Godeps/_workspace/src/github.com/docker/docker/pkg/term/MAINTAINERS
generated
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Solomon Hykes <solomon@docker.com> (@shykes)
|
87
Godeps/_workspace/src/github.com/docker/docker/pkg/term/console_windows.go
generated
vendored
Normal file
87
Godeps/_workspace/src/github.com/docker/docker/pkg/term/console_windows.go
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package term
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Consts for Get/SetConsoleMode function
|
||||||
|
// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx
|
||||||
|
ENABLE_ECHO_INPUT = 0x0004
|
||||||
|
ENABLE_INSERT_MODE = 0x0020
|
||||||
|
ENABLE_LINE_INPUT = 0x0002
|
||||||
|
ENABLE_MOUSE_INPUT = 0x0010
|
||||||
|
ENABLE_PROCESSED_INPUT = 0x0001
|
||||||
|
ENABLE_QUICK_EDIT_MODE = 0x0040
|
||||||
|
ENABLE_WINDOW_INPUT = 0x0008
|
||||||
|
// If parameter is a screen buffer handle, additional values
|
||||||
|
ENABLE_PROCESSED_OUTPUT = 0x0001
|
||||||
|
ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002
|
||||||
|
)
|
||||||
|
|
||||||
|
var kernel32DLL = syscall.NewLazyDLL("kernel32.dll")
|
||||||
|
|
||||||
|
var (
|
||||||
|
setConsoleModeProc = kernel32DLL.NewProc("SetConsoleMode")
|
||||||
|
getConsoleScreenBufferInfoProc = kernel32DLL.NewProc("GetConsoleScreenBufferInfo")
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetConsoleMode(fileDesc uintptr) (uint32, error) {
|
||||||
|
var mode uint32
|
||||||
|
err := syscall.GetConsoleMode(syscall.Handle(fileDesc), &mode)
|
||||||
|
return mode, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetConsoleMode(fileDesc uintptr, mode uint32) error {
|
||||||
|
r, _, err := setConsoleModeProc.Call(fileDesc, uintptr(mode), 0)
|
||||||
|
if r == 0 {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// types for calling GetConsoleScreenBufferInfo
|
||||||
|
// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms682093(v=vs.85).aspx
|
||||||
|
type (
|
||||||
|
SHORT int16
|
||||||
|
|
||||||
|
SMALL_RECT struct {
|
||||||
|
Left SHORT
|
||||||
|
Top SHORT
|
||||||
|
Right SHORT
|
||||||
|
Bottom SHORT
|
||||||
|
}
|
||||||
|
|
||||||
|
COORD struct {
|
||||||
|
X SHORT
|
||||||
|
Y SHORT
|
||||||
|
}
|
||||||
|
|
||||||
|
WORD uint16
|
||||||
|
|
||||||
|
CONSOLE_SCREEN_BUFFER_INFO struct {
|
||||||
|
dwSize COORD
|
||||||
|
dwCursorPosition COORD
|
||||||
|
wAttributes WORD
|
||||||
|
srWindow SMALL_RECT
|
||||||
|
dwMaximumWindowSize COORD
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetConsoleScreenBufferInfo(fileDesc uintptr) (*CONSOLE_SCREEN_BUFFER_INFO, error) {
|
||||||
|
var info CONSOLE_SCREEN_BUFFER_INFO
|
||||||
|
r, _, err := getConsoleScreenBufferInfoProc.Call(uintptr(fileDesc), uintptr(unsafe.Pointer(&info)), 0)
|
||||||
|
if r == 0 {
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, syscall.EINVAL
|
||||||
|
}
|
||||||
|
return &info, nil
|
||||||
|
}
|
47
Godeps/_workspace/src/github.com/docker/docker/pkg/term/tc_linux_cgo.go
generated
vendored
Normal file
47
Godeps/_workspace/src/github.com/docker/docker/pkg/term/tc_linux_cgo.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// +build linux,cgo
|
||||||
|
|
||||||
|
package term
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// #include <termios.h>
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
type Termios syscall.Termios
|
||||||
|
|
||||||
|
// MakeRaw put the terminal connected to the given file descriptor into raw
|
||||||
|
// mode and returns the previous state of the terminal so that it can be
|
||||||
|
// restored.
|
||||||
|
func MakeRaw(fd uintptr) (*State, error) {
|
||||||
|
var oldState State
|
||||||
|
if err := tcget(fd, &oldState.termios); err != 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
newState := oldState.termios
|
||||||
|
|
||||||
|
C.cfmakeraw((*C.struct_termios)(unsafe.Pointer(&newState)))
|
||||||
|
if err := tcset(fd, &newState); err != 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &oldState, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func tcget(fd uintptr, p *Termios) syscall.Errno {
|
||||||
|
ret, err := C.tcgetattr(C.int(fd), (*C.struct_termios)(unsafe.Pointer(p)))
|
||||||
|
if ret != 0 {
|
||||||
|
return err.(syscall.Errno)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func tcset(fd uintptr, p *Termios) syscall.Errno {
|
||||||
|
ret, err := C.tcsetattr(C.int(fd), C.TCSANOW, (*C.struct_termios)(unsafe.Pointer(p)))
|
||||||
|
if ret != 0 {
|
||||||
|
return err.(syscall.Errno)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
19
Godeps/_workspace/src/github.com/docker/docker/pkg/term/tc_other.go
generated
vendored
Normal file
19
Godeps/_workspace/src/github.com/docker/docker/pkg/term/tc_other.go
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
// +build !windows
|
||||||
|
// +build !linux !cgo
|
||||||
|
|
||||||
|
package term
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func tcget(fd uintptr, p *Termios) syscall.Errno {
|
||||||
|
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(p)))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func tcset(fd uintptr, p *Termios) syscall.Errno {
|
||||||
|
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(p)))
|
||||||
|
return err
|
||||||
|
}
|
103
Godeps/_workspace/src/github.com/docker/docker/pkg/term/term.go
generated
vendored
Normal file
103
Godeps/_workspace/src/github.com/docker/docker/pkg/term/term.go
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package term
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidState = errors.New("Invalid terminal state")
|
||||||
|
)
|
||||||
|
|
||||||
|
type State struct {
|
||||||
|
termios Termios
|
||||||
|
}
|
||||||
|
|
||||||
|
type Winsize struct {
|
||||||
|
Height uint16
|
||||||
|
Width uint16
|
||||||
|
x uint16
|
||||||
|
y uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWinsize(fd uintptr) (*Winsize, error) {
|
||||||
|
ws := &Winsize{}
|
||||||
|
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(ws)))
|
||||||
|
// Skipp errno = 0
|
||||||
|
if err == 0 {
|
||||||
|
return ws, nil
|
||||||
|
}
|
||||||
|
return ws, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetWinsize(fd uintptr, ws *Winsize) error {
|
||||||
|
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TIOCSWINSZ), uintptr(unsafe.Pointer(ws)))
|
||||||
|
// Skipp errno = 0
|
||||||
|
if err == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||||
|
func IsTerminal(fd uintptr) bool {
|
||||||
|
var termios Termios
|
||||||
|
return tcget(fd, &termios) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore restores the terminal connected to the given file descriptor to a
|
||||||
|
// previous state.
|
||||||
|
func RestoreTerminal(fd uintptr, state *State) error {
|
||||||
|
if state == nil {
|
||||||
|
return ErrInvalidState
|
||||||
|
}
|
||||||
|
if err := tcset(fd, &state.termios); err != 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SaveState(fd uintptr) (*State, error) {
|
||||||
|
var oldState State
|
||||||
|
if err := tcget(fd, &oldState.termios); err != 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &oldState, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DisableEcho(fd uintptr, state *State) error {
|
||||||
|
newState := state.termios
|
||||||
|
newState.Lflag &^= syscall.ECHO
|
||||||
|
|
||||||
|
if err := tcset(fd, &newState); err != 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
handleInterrupt(fd, state)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetRawTerminal(fd uintptr) (*State, error) {
|
||||||
|
oldState, err := MakeRaw(fd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
handleInterrupt(fd, oldState)
|
||||||
|
return oldState, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleInterrupt(fd uintptr, state *State) {
|
||||||
|
sigchan := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigchan, os.Interrupt)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
_ = <-sigchan
|
||||||
|
RestoreTerminal(fd, state)
|
||||||
|
os.Exit(0)
|
||||||
|
}()
|
||||||
|
}
|
89
Godeps/_workspace/src/github.com/docker/docker/pkg/term/term_windows.go
generated
vendored
Normal file
89
Godeps/_workspace/src/github.com/docker/docker/pkg/term/term_windows.go
generated
vendored
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package term
|
||||||
|
|
||||||
|
type State struct {
|
||||||
|
mode uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type Winsize struct {
|
||||||
|
Height uint16
|
||||||
|
Width uint16
|
||||||
|
x uint16
|
||||||
|
y uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWinsize(fd uintptr) (*Winsize, error) {
|
||||||
|
ws := &Winsize{}
|
||||||
|
var info *CONSOLE_SCREEN_BUFFER_INFO
|
||||||
|
info, err := GetConsoleScreenBufferInfo(fd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ws.Height = uint16(info.srWindow.Right - info.srWindow.Left + 1)
|
||||||
|
ws.Width = uint16(info.srWindow.Bottom - info.srWindow.Top + 1)
|
||||||
|
|
||||||
|
ws.x = 0 // todo azlinux -- this is the pixel size of the Window, and not currently used by any caller
|
||||||
|
ws.y = 0
|
||||||
|
|
||||||
|
return ws, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetWinsize(fd uintptr, ws *Winsize) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||||
|
func IsTerminal(fd uintptr) bool {
|
||||||
|
_, e := GetConsoleMode(fd)
|
||||||
|
return e == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore restores the terminal connected to the given file descriptor to a
|
||||||
|
// previous state.
|
||||||
|
func RestoreTerminal(fd uintptr, state *State) error {
|
||||||
|
return SetConsoleMode(fd, state.mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SaveState(fd uintptr) (*State, error) {
|
||||||
|
mode, e := GetConsoleMode(fd)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
return &State{mode}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx for these flag settings
|
||||||
|
func DisableEcho(fd uintptr, state *State) error {
|
||||||
|
state.mode &^= (ENABLE_ECHO_INPUT)
|
||||||
|
state.mode |= (ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT)
|
||||||
|
return SetConsoleMode(fd, state.mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetRawTerminal(fd uintptr) (*State, error) {
|
||||||
|
oldState, err := MakeRaw(fd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// TODO (azlinux): implement handling interrupt and restore state of terminal
|
||||||
|
return oldState, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeRaw puts the terminal connected to the given file descriptor into raw
|
||||||
|
// mode and returns the previous state of the terminal so that it can be
|
||||||
|
// restored.
|
||||||
|
func MakeRaw(fd uintptr) (*State, error) {
|
||||||
|
var state *State
|
||||||
|
state, err := SaveState(fd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx for these flag settings
|
||||||
|
state.mode &^= (ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT)
|
||||||
|
err = SetConsoleMode(fd, state.mode)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return state, nil
|
||||||
|
}
|
65
Godeps/_workspace/src/github.com/docker/docker/pkg/term/termios_darwin.go
generated
vendored
Normal file
65
Godeps/_workspace/src/github.com/docker/docker/pkg/term/termios_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
package term
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
getTermios = syscall.TIOCGETA
|
||||||
|
setTermios = syscall.TIOCSETA
|
||||||
|
|
||||||
|
IGNBRK = syscall.IGNBRK
|
||||||
|
PARMRK = syscall.PARMRK
|
||||||
|
INLCR = syscall.INLCR
|
||||||
|
IGNCR = syscall.IGNCR
|
||||||
|
ECHONL = syscall.ECHONL
|
||||||
|
CSIZE = syscall.CSIZE
|
||||||
|
ICRNL = syscall.ICRNL
|
||||||
|
ISTRIP = syscall.ISTRIP
|
||||||
|
PARENB = syscall.PARENB
|
||||||
|
ECHO = syscall.ECHO
|
||||||
|
ICANON = syscall.ICANON
|
||||||
|
ISIG = syscall.ISIG
|
||||||
|
IXON = syscall.IXON
|
||||||
|
BRKINT = syscall.BRKINT
|
||||||
|
INPCK = syscall.INPCK
|
||||||
|
OPOST = syscall.OPOST
|
||||||
|
CS8 = syscall.CS8
|
||||||
|
IEXTEN = syscall.IEXTEN
|
||||||
|
)
|
||||||
|
|
||||||
|
type Termios struct {
|
||||||
|
Iflag uint64
|
||||||
|
Oflag uint64
|
||||||
|
Cflag uint64
|
||||||
|
Lflag uint64
|
||||||
|
Cc [20]byte
|
||||||
|
Ispeed uint64
|
||||||
|
Ospeed uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeRaw put the terminal connected to the given file descriptor into raw
|
||||||
|
// mode and returns the previous state of the terminal so that it can be
|
||||||
|
// restored.
|
||||||
|
func MakeRaw(fd uintptr) (*State, error) {
|
||||||
|
var oldState State
|
||||||
|
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
newState := oldState.termios
|
||||||
|
newState.Iflag &^= (IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON)
|
||||||
|
newState.Oflag &^= OPOST
|
||||||
|
newState.Lflag &^= (ECHO | ECHONL | ICANON | ISIG | IEXTEN)
|
||||||
|
newState.Cflag &^= (CSIZE | PARENB)
|
||||||
|
newState.Cflag |= CS8
|
||||||
|
newState.Cc[syscall.VMIN] = 1
|
||||||
|
newState.Cc[syscall.VTIME] = 0
|
||||||
|
|
||||||
|
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(setTermios), uintptr(unsafe.Pointer(&newState))); err != 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &oldState, nil
|
||||||
|
}
|
65
Godeps/_workspace/src/github.com/docker/docker/pkg/term/termios_freebsd.go
generated
vendored
Normal file
65
Godeps/_workspace/src/github.com/docker/docker/pkg/term/termios_freebsd.go
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
package term
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
getTermios = syscall.TIOCGETA
|
||||||
|
setTermios = syscall.TIOCSETA
|
||||||
|
|
||||||
|
IGNBRK = syscall.IGNBRK
|
||||||
|
PARMRK = syscall.PARMRK
|
||||||
|
INLCR = syscall.INLCR
|
||||||
|
IGNCR = syscall.IGNCR
|
||||||
|
ECHONL = syscall.ECHONL
|
||||||
|
CSIZE = syscall.CSIZE
|
||||||
|
ICRNL = syscall.ICRNL
|
||||||
|
ISTRIP = syscall.ISTRIP
|
||||||
|
PARENB = syscall.PARENB
|
||||||
|
ECHO = syscall.ECHO
|
||||||
|
ICANON = syscall.ICANON
|
||||||
|
ISIG = syscall.ISIG
|
||||||
|
IXON = syscall.IXON
|
||||||
|
BRKINT = syscall.BRKINT
|
||||||
|
INPCK = syscall.INPCK
|
||||||
|
OPOST = syscall.OPOST
|
||||||
|
CS8 = syscall.CS8
|
||||||
|
IEXTEN = syscall.IEXTEN
|
||||||
|
)
|
||||||
|
|
||||||
|
type Termios struct {
|
||||||
|
Iflag uint32
|
||||||
|
Oflag uint32
|
||||||
|
Cflag uint32
|
||||||
|
Lflag uint32
|
||||||
|
Cc [20]byte
|
||||||
|
Ispeed uint32
|
||||||
|
Ospeed uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeRaw put the terminal connected to the given file descriptor into raw
|
||||||
|
// mode and returns the previous state of the terminal so that it can be
|
||||||
|
// restored.
|
||||||
|
func MakeRaw(fd uintptr) (*State, error) {
|
||||||
|
var oldState State
|
||||||
|
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
newState := oldState.termios
|
||||||
|
newState.Iflag &^= (IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON)
|
||||||
|
newState.Oflag &^= OPOST
|
||||||
|
newState.Lflag &^= (ECHO | ECHONL | ICANON | ISIG | IEXTEN)
|
||||||
|
newState.Cflag &^= (CSIZE | PARENB)
|
||||||
|
newState.Cflag |= CS8
|
||||||
|
newState.Cc[syscall.VMIN] = 1
|
||||||
|
newState.Cc[syscall.VTIME] = 0
|
||||||
|
|
||||||
|
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(setTermios), uintptr(unsafe.Pointer(&newState))); err != 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &oldState, nil
|
||||||
|
}
|
46
Godeps/_workspace/src/github.com/docker/docker/pkg/term/termios_linux.go
generated
vendored
Normal file
46
Godeps/_workspace/src/github.com/docker/docker/pkg/term/termios_linux.go
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
// +build !cgo
|
||||||
|
|
||||||
|
package term
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
getTermios = syscall.TCGETS
|
||||||
|
setTermios = syscall.TCSETS
|
||||||
|
)
|
||||||
|
|
||||||
|
type Termios struct {
|
||||||
|
Iflag uint32
|
||||||
|
Oflag uint32
|
||||||
|
Cflag uint32
|
||||||
|
Lflag uint32
|
||||||
|
Cc [20]byte
|
||||||
|
Ispeed uint32
|
||||||
|
Ospeed uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeRaw put the terminal connected to the given file descriptor into raw
|
||||||
|
// mode and returns the previous state of the terminal so that it can be
|
||||||
|
// restored.
|
||||||
|
func MakeRaw(fd uintptr) (*State, error) {
|
||||||
|
var oldState State
|
||||||
|
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, getTermios, uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
newState := oldState.termios
|
||||||
|
|
||||||
|
newState.Iflag &^= (syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON)
|
||||||
|
newState.Oflag &^= syscall.OPOST
|
||||||
|
newState.Lflag &^= (syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN)
|
||||||
|
newState.Cflag &^= (syscall.CSIZE | syscall.PARENB)
|
||||||
|
newState.Cflag |= syscall.CS8
|
||||||
|
|
||||||
|
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(&newState))); err != 0 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &oldState, nil
|
||||||
|
}
|
13
Godeps/_workspace/src/github.com/docker/spdystream/CONTRIBUTING.md
generated
vendored
Normal file
13
Godeps/_workspace/src/github.com/docker/spdystream/CONTRIBUTING.md
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Contributing to SpdyStream
|
||||||
|
|
||||||
|
Want to hack on spdystream? Awesome! Here are instructions to get you
|
||||||
|
started.
|
||||||
|
|
||||||
|
SpdyStream is a part of the [Docker](https://docker.io) project, and follows
|
||||||
|
the same rules and principles. If you're already familiar with the way
|
||||||
|
Docker does things, you'll feel right at home.
|
||||||
|
|
||||||
|
Otherwise, go read
|
||||||
|
[Docker's contributions guidelines](https://github.com/dotcloud/docker/blob/master/CONTRIBUTING.md).
|
||||||
|
|
||||||
|
Happy hacking!
|
191
Godeps/_workspace/src/github.com/docker/spdystream/LICENSE
generated
vendored
Normal file
191
Godeps/_workspace/src/github.com/docker/spdystream/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
Copyright 2014 Docker, Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
1
Godeps/_workspace/src/github.com/docker/spdystream/MAINTAINERS
generated
vendored
Normal file
1
Godeps/_workspace/src/github.com/docker/spdystream/MAINTAINERS
generated
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Derek McGowan <derek@docker.com> (@dmcg)
|
78
Godeps/_workspace/src/github.com/docker/spdystream/README.md
generated
vendored
Normal file
78
Godeps/_workspace/src/github.com/docker/spdystream/README.md
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
# SpdyStream
|
||||||
|
|
||||||
|
A multiplexed stream library using spdy
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Client example (connecting to mirroring server without auth)
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/docker/spdystream"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
conn, err := net.Dial("tcp", "localhost:8080")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
spdyConn, err := spdystream.NewConnection(conn, false)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
go spdyConn.Serve(spdystream.NoOpStreamHandler)
|
||||||
|
stream, err := spdyConn.CreateStream(http.Header{}, nil, false)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.Wait()
|
||||||
|
|
||||||
|
fmt.Fprint(stream, "Writing to stream")
|
||||||
|
|
||||||
|
buf := make([]byte, 25)
|
||||||
|
stream.Read(buf)
|
||||||
|
fmt.Println(string(buf))
|
||||||
|
|
||||||
|
stream.Close()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Server example (mirroring server without auth)
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/docker/spdystream"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
listener, err := net.Listen("tcp", "localhost:8080")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
conn, err := listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
spdyConn, err := spdystream.NewConnection(conn, true)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
go spdyConn.Serve(spdystream.MirrorStreamHandler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Copyright and license
|
||||||
|
|
||||||
|
Code and documentation copyright 2013-2014 Docker, inc. Code released under the Apache 2.0 license.
|
||||||
|
Docs released under Creative commons.
|
877
Godeps/_workspace/src/github.com/docker/spdystream/connection.go
generated
vendored
Normal file
877
Godeps/_workspace/src/github.com/docker/spdystream/connection.go
generated
vendored
Normal file
@@ -0,0 +1,877 @@
|
|||||||
|
package spdystream
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.google.com/p/go.net/spdy"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidStreamId = errors.New("Invalid stream id")
|
||||||
|
ErrTimeout = errors.New("Timeout occured")
|
||||||
|
ErrReset = errors.New("Stream reset")
|
||||||
|
ErrWriteClosedStream = errors.New("Write on closed stream")
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
FRAME_WORKERS = 5
|
||||||
|
QUEUE_SIZE = 50
|
||||||
|
)
|
||||||
|
|
||||||
|
type StreamHandler func(stream *Stream)
|
||||||
|
|
||||||
|
type AuthHandler func(header http.Header, slot uint8, parent uint32) bool
|
||||||
|
|
||||||
|
type idleAwareFramer struct {
|
||||||
|
f *spdy.Framer
|
||||||
|
conn *Connection
|
||||||
|
resetChan chan struct{}
|
||||||
|
setTimeoutChan chan time.Duration
|
||||||
|
timeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func newIdleAwareFramer(framer *spdy.Framer) *idleAwareFramer {
|
||||||
|
iaf := &idleAwareFramer{
|
||||||
|
f: framer,
|
||||||
|
resetChan: make(chan struct{}, 2),
|
||||||
|
setTimeoutChan: make(chan time.Duration),
|
||||||
|
}
|
||||||
|
return iaf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *idleAwareFramer) monitor() {
|
||||||
|
var (
|
||||||
|
timer *time.Timer
|
||||||
|
expired <-chan time.Time
|
||||||
|
)
|
||||||
|
Loop:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case timeout := <-i.setTimeoutChan:
|
||||||
|
i.timeout = timeout
|
||||||
|
if timeout == 0 {
|
||||||
|
if timer != nil {
|
||||||
|
timer.Stop()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if timer == nil {
|
||||||
|
timer = time.NewTimer(timeout)
|
||||||
|
expired = timer.C
|
||||||
|
} else {
|
||||||
|
timer.Reset(timeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case <-i.resetChan:
|
||||||
|
if timer != nil && i.timeout > 0 {
|
||||||
|
timer.Reset(i.timeout)
|
||||||
|
}
|
||||||
|
case <-expired:
|
||||||
|
for _, stream := range i.conn.streams {
|
||||||
|
stream.Reset()
|
||||||
|
}
|
||||||
|
i.conn.Close()
|
||||||
|
break Loop
|
||||||
|
case <-i.conn.closeChan:
|
||||||
|
if timer != nil {
|
||||||
|
timer.Stop()
|
||||||
|
}
|
||||||
|
break Loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *idleAwareFramer) WriteFrame(frame spdy.Frame) error {
|
||||||
|
err := i.f.WriteFrame(frame)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
i.resetChan <- struct{}{}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *idleAwareFramer) ReadFrame() (spdy.Frame, error) {
|
||||||
|
frame, err := i.f.ReadFrame()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
i.resetChan <- struct{}{}
|
||||||
|
|
||||||
|
return frame, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Connection struct {
|
||||||
|
conn net.Conn
|
||||||
|
framer *idleAwareFramer
|
||||||
|
writeLock sync.Mutex
|
||||||
|
|
||||||
|
closeChan chan bool
|
||||||
|
goneAway bool
|
||||||
|
lastStreamChan chan<- *Stream
|
||||||
|
goAwayTimeout time.Duration
|
||||||
|
closeTimeout time.Duration
|
||||||
|
|
||||||
|
streamLock *sync.RWMutex
|
||||||
|
streamCond *sync.Cond
|
||||||
|
streams map[spdy.StreamId]*Stream
|
||||||
|
|
||||||
|
nextIdLock sync.Mutex
|
||||||
|
receiveIdLock sync.Mutex
|
||||||
|
nextStreamId spdy.StreamId
|
||||||
|
receivedStreamId spdy.StreamId
|
||||||
|
|
||||||
|
pingIdLock sync.Mutex
|
||||||
|
pingId uint32
|
||||||
|
pingChans map[uint32]chan error
|
||||||
|
|
||||||
|
shutdownLock sync.Mutex
|
||||||
|
shutdownChan chan error
|
||||||
|
hasShutdown bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConnection creates a new spdy connection from an existing
|
||||||
|
// network connection.
|
||||||
|
func NewConnection(conn net.Conn, server bool) (*Connection, error) {
|
||||||
|
framer, framerErr := spdy.NewFramer(conn, conn)
|
||||||
|
if framerErr != nil {
|
||||||
|
return nil, framerErr
|
||||||
|
}
|
||||||
|
idleAwareFramer := newIdleAwareFramer(framer)
|
||||||
|
var sid spdy.StreamId
|
||||||
|
var rid spdy.StreamId
|
||||||
|
var pid uint32
|
||||||
|
if server {
|
||||||
|
sid = 2
|
||||||
|
rid = 1
|
||||||
|
pid = 2
|
||||||
|
} else {
|
||||||
|
sid = 1
|
||||||
|
rid = 2
|
||||||
|
pid = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
streamLock := new(sync.RWMutex)
|
||||||
|
streamCond := sync.NewCond(streamLock)
|
||||||
|
|
||||||
|
session := &Connection{
|
||||||
|
conn: conn,
|
||||||
|
framer: idleAwareFramer,
|
||||||
|
|
||||||
|
closeChan: make(chan bool),
|
||||||
|
goAwayTimeout: time.Duration(0),
|
||||||
|
closeTimeout: time.Duration(0),
|
||||||
|
|
||||||
|
streamLock: streamLock,
|
||||||
|
streamCond: streamCond,
|
||||||
|
streams: make(map[spdy.StreamId]*Stream),
|
||||||
|
nextStreamId: sid,
|
||||||
|
receivedStreamId: rid,
|
||||||
|
|
||||||
|
pingId: pid,
|
||||||
|
pingChans: make(map[uint32]chan error),
|
||||||
|
|
||||||
|
shutdownChan: make(chan error),
|
||||||
|
}
|
||||||
|
idleAwareFramer.conn = session
|
||||||
|
go idleAwareFramer.monitor()
|
||||||
|
|
||||||
|
return session, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ping sends a ping frame across the connection and
|
||||||
|
// returns the response time
|
||||||
|
func (s *Connection) Ping() (time.Duration, error) {
|
||||||
|
pid := s.pingId
|
||||||
|
s.pingIdLock.Lock()
|
||||||
|
if s.pingId > 0x7ffffffe {
|
||||||
|
s.pingId = s.pingId - 0x7ffffffe
|
||||||
|
} else {
|
||||||
|
s.pingId = s.pingId + 2
|
||||||
|
}
|
||||||
|
s.pingIdLock.Unlock()
|
||||||
|
pingChan := make(chan error)
|
||||||
|
s.pingChans[pid] = pingChan
|
||||||
|
defer delete(s.pingChans, pid)
|
||||||
|
|
||||||
|
frame := &spdy.PingFrame{Id: pid}
|
||||||
|
startTime := time.Now()
|
||||||
|
s.writeLock.Lock()
|
||||||
|
writeErr := s.framer.WriteFrame(frame)
|
||||||
|
s.writeLock.Unlock()
|
||||||
|
if writeErr != nil {
|
||||||
|
return time.Duration(0), writeErr
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-s.closeChan:
|
||||||
|
return time.Duration(0), errors.New("connection closed")
|
||||||
|
case err, ok := <-pingChan:
|
||||||
|
if ok && err != nil {
|
||||||
|
return time.Duration(0), err
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return time.Now().Sub(startTime), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serve handles frames sent from the server, including reply frames
|
||||||
|
// which are needed to fully initiate connections. Both clients and servers
|
||||||
|
// should call Serve in a separate goroutine before creating streams.
|
||||||
|
func (s *Connection) Serve(newHandler StreamHandler) {
|
||||||
|
// Parition queues to ensure stream frames are handled
|
||||||
|
// by the same worker, ensuring order is maintained
|
||||||
|
frameQueues := make([]*PriorityFrameQueue, FRAME_WORKERS)
|
||||||
|
for i := 0; i < FRAME_WORKERS; i++ {
|
||||||
|
frameQueues[i] = NewPriorityFrameQueue(QUEUE_SIZE)
|
||||||
|
// Ensure frame queue is drained when connection is closed
|
||||||
|
go func(frameQueue *PriorityFrameQueue) {
|
||||||
|
<-s.closeChan
|
||||||
|
frameQueue.Drain()
|
||||||
|
}(frameQueues[i])
|
||||||
|
|
||||||
|
go s.frameHandler(frameQueues[i], newHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
var partitionRoundRobin int
|
||||||
|
for {
|
||||||
|
readFrame, err := s.framer.ReadFrame()
|
||||||
|
if err != nil {
|
||||||
|
if err != io.EOF {
|
||||||
|
fmt.Errorf("frame read error: %s", err)
|
||||||
|
} else {
|
||||||
|
debugMessage("EOF received")
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
var priority uint8
|
||||||
|
var partition int
|
||||||
|
switch frame := readFrame.(type) {
|
||||||
|
case *spdy.SynStreamFrame:
|
||||||
|
if s.checkStreamFrame(frame) {
|
||||||
|
priority = frame.Priority
|
||||||
|
partition = int(frame.StreamId % FRAME_WORKERS)
|
||||||
|
debugMessage("(%p) Add stream frame: %d ", s, frame.StreamId)
|
||||||
|
s.addStreamFrame(frame)
|
||||||
|
} else {
|
||||||
|
debugMessage("(%p) Rejected stream frame: %d ", s, frame.StreamId)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case *spdy.SynReplyFrame:
|
||||||
|
priority = s.getStreamPriority(frame.StreamId)
|
||||||
|
partition = int(frame.StreamId % FRAME_WORKERS)
|
||||||
|
case *spdy.DataFrame:
|
||||||
|
priority = s.getStreamPriority(frame.StreamId)
|
||||||
|
partition = int(frame.StreamId % FRAME_WORKERS)
|
||||||
|
case *spdy.RstStreamFrame:
|
||||||
|
priority = s.getStreamPriority(frame.StreamId)
|
||||||
|
partition = int(frame.StreamId % FRAME_WORKERS)
|
||||||
|
case *spdy.HeadersFrame:
|
||||||
|
priority = s.getStreamPriority(frame.StreamId)
|
||||||
|
partition = int(frame.StreamId % FRAME_WORKERS)
|
||||||
|
case *spdy.PingFrame:
|
||||||
|
priority = 0
|
||||||
|
partition = partitionRoundRobin
|
||||||
|
partitionRoundRobin = (partitionRoundRobin + 1) % FRAME_WORKERS
|
||||||
|
case *spdy.GoAwayFrame:
|
||||||
|
priority = 0
|
||||||
|
partition = partitionRoundRobin
|
||||||
|
partitionRoundRobin = (partitionRoundRobin + 1) % FRAME_WORKERS
|
||||||
|
default:
|
||||||
|
priority = 7
|
||||||
|
partition = partitionRoundRobin
|
||||||
|
partitionRoundRobin = (partitionRoundRobin + 1) % FRAME_WORKERS
|
||||||
|
}
|
||||||
|
frameQueues[partition].Push(readFrame, priority)
|
||||||
|
}
|
||||||
|
close(s.closeChan)
|
||||||
|
|
||||||
|
s.streamCond.L.Lock()
|
||||||
|
// notify streams that they're now closed, which will
|
||||||
|
// unblock any stream Read() calls
|
||||||
|
for _, stream := range s.streams {
|
||||||
|
stream.closeRemoteChannels()
|
||||||
|
}
|
||||||
|
s.streams = make(map[spdy.StreamId]*Stream)
|
||||||
|
s.streamCond.Broadcast()
|
||||||
|
s.streamCond.L.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Connection) frameHandler(frameQueue *PriorityFrameQueue, newHandler StreamHandler) {
|
||||||
|
for {
|
||||||
|
popFrame := frameQueue.Pop()
|
||||||
|
if popFrame == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var frameErr error
|
||||||
|
switch frame := popFrame.(type) {
|
||||||
|
case *spdy.SynStreamFrame:
|
||||||
|
frameErr = s.handleStreamFrame(frame, newHandler)
|
||||||
|
case *spdy.SynReplyFrame:
|
||||||
|
frameErr = s.handleReplyFrame(frame)
|
||||||
|
case *spdy.DataFrame:
|
||||||
|
frameErr = s.handleDataFrame(frame)
|
||||||
|
case *spdy.RstStreamFrame:
|
||||||
|
frameErr = s.handleResetFrame(frame)
|
||||||
|
case *spdy.HeadersFrame:
|
||||||
|
frameErr = s.handleHeaderFrame(frame)
|
||||||
|
case *spdy.PingFrame:
|
||||||
|
frameErr = s.handlePingFrame(frame)
|
||||||
|
case *spdy.GoAwayFrame:
|
||||||
|
frameErr = s.handleGoAwayFrame(frame)
|
||||||
|
default:
|
||||||
|
frameErr = fmt.Errorf("unhandled frame type: %T", frame)
|
||||||
|
}
|
||||||
|
|
||||||
|
if frameErr != nil {
|
||||||
|
fmt.Errorf("frame handling error: %s", frameErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Connection) getStreamPriority(streamId spdy.StreamId) uint8 {
|
||||||
|
stream, streamOk := s.getStream(streamId)
|
||||||
|
if !streamOk {
|
||||||
|
return 7
|
||||||
|
}
|
||||||
|
return stream.priority
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Connection) addStreamFrame(frame *spdy.SynStreamFrame) {
|
||||||
|
var parent *Stream
|
||||||
|
if frame.AssociatedToStreamId != spdy.StreamId(0) {
|
||||||
|
parent, _ = s.getStream(frame.AssociatedToStreamId)
|
||||||
|
}
|
||||||
|
|
||||||
|
stream := &Stream{
|
||||||
|
streamId: frame.StreamId,
|
||||||
|
parent: parent,
|
||||||
|
conn: s,
|
||||||
|
startChan: make(chan error),
|
||||||
|
headers: frame.Headers,
|
||||||
|
finished: (frame.CFHeader.Flags & spdy.ControlFlagUnidirectional) != 0x00,
|
||||||
|
replyCond: sync.NewCond(new(sync.Mutex)),
|
||||||
|
dataChan: make(chan []byte),
|
||||||
|
headerChan: make(chan http.Header),
|
||||||
|
closeChan: make(chan bool),
|
||||||
|
}
|
||||||
|
if frame.CFHeader.Flags&spdy.ControlFlagFin != 0x00 {
|
||||||
|
stream.closeRemoteChannels()
|
||||||
|
}
|
||||||
|
|
||||||
|
s.addStream(stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkStreamFrame checks to see if a stream frame is allowed.
|
||||||
|
// If the stream is invalid, then a reset frame with protocol error
|
||||||
|
// will be returned.
|
||||||
|
func (s *Connection) checkStreamFrame(frame *spdy.SynStreamFrame) bool {
|
||||||
|
s.receiveIdLock.Lock()
|
||||||
|
defer s.receiveIdLock.Unlock()
|
||||||
|
if s.goneAway {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
validationErr := s.validateStreamId(frame.StreamId)
|
||||||
|
if validationErr != nil {
|
||||||
|
go func() {
|
||||||
|
resetErr := s.sendResetFrame(spdy.ProtocolError, frame.StreamId)
|
||||||
|
if resetErr != nil {
|
||||||
|
fmt.Errorf("reset error: %s", resetErr)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Connection) handleStreamFrame(frame *spdy.SynStreamFrame, newHandler StreamHandler) error {
|
||||||
|
stream, ok := s.getStream(frame.StreamId)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Missing stream: %d", frame.StreamId)
|
||||||
|
}
|
||||||
|
|
||||||
|
newHandler(stream)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Connection) handleReplyFrame(frame *spdy.SynReplyFrame) error {
|
||||||
|
debugMessage("(%p) Reply frame received for %d", s, frame.StreamId)
|
||||||
|
stream, streamOk := s.getStream(frame.StreamId)
|
||||||
|
if !streamOk {
|
||||||
|
debugMessage("Reply frame gone away for %d", frame.StreamId)
|
||||||
|
// Stream has already gone away
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if stream.replied {
|
||||||
|
// Stream has already received reply
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
stream.replied = true
|
||||||
|
|
||||||
|
// TODO Check for error
|
||||||
|
if (frame.CFHeader.Flags & spdy.ControlFlagFin) != 0x00 {
|
||||||
|
s.remoteStreamFinish(stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
close(stream.startChan)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Connection) handleResetFrame(frame *spdy.RstStreamFrame) error {
|
||||||
|
stream, streamOk := s.getStream(frame.StreamId)
|
||||||
|
if !streamOk {
|
||||||
|
// Stream has already been removed
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
s.removeStream(stream)
|
||||||
|
stream.closeRemoteChannels()
|
||||||
|
|
||||||
|
if !stream.replied {
|
||||||
|
stream.replied = true
|
||||||
|
stream.startChan <- ErrReset
|
||||||
|
close(stream.startChan)
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.finishLock.Lock()
|
||||||
|
stream.finished = true
|
||||||
|
stream.finishLock.Unlock()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Connection) handleHeaderFrame(frame *spdy.HeadersFrame) error {
|
||||||
|
stream, streamOk := s.getStream(frame.StreamId)
|
||||||
|
if !streamOk {
|
||||||
|
// Stream has already gone away
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if !stream.replied {
|
||||||
|
// No reply received...Protocol error?
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO limit headers while not blocking (use buffered chan or goroutine?)
|
||||||
|
select {
|
||||||
|
case <-stream.closeChan:
|
||||||
|
return nil
|
||||||
|
case stream.headerChan <- frame.Headers:
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frame.CFHeader.Flags & spdy.ControlFlagFin) != 0x00 {
|
||||||
|
s.remoteStreamFinish(stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Connection) handleDataFrame(frame *spdy.DataFrame) error {
|
||||||
|
debugMessage("(%p) Data frame received for %d", s, frame.StreamId)
|
||||||
|
stream, streamOk := s.getStream(frame.StreamId)
|
||||||
|
if !streamOk {
|
||||||
|
debugMessage("Data frame gone away for %d", frame.StreamId)
|
||||||
|
// Stream has already gone away
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if !stream.replied {
|
||||||
|
debugMessage("Data frame not replied %d", frame.StreamId)
|
||||||
|
// No reply received...Protocol error?
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
debugMessage("(%p) (%d) Data frame handling", stream, stream.streamId)
|
||||||
|
if len(frame.Data) > 0 {
|
||||||
|
stream.dataLock.RLock()
|
||||||
|
select {
|
||||||
|
case <-stream.closeChan:
|
||||||
|
debugMessage("(%p) (%d) Data frame not sent (stream shut down)", stream, stream.streamId)
|
||||||
|
case stream.dataChan <- frame.Data:
|
||||||
|
debugMessage("(%p) (%d) Data frame sent", stream, stream.streamId)
|
||||||
|
}
|
||||||
|
stream.dataLock.RUnlock()
|
||||||
|
}
|
||||||
|
if (frame.Flags & spdy.DataFlagFin) != 0x00 {
|
||||||
|
s.remoteStreamFinish(stream)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Connection) handlePingFrame(frame *spdy.PingFrame) error {
|
||||||
|
if s.pingId&0x01 != frame.Id&0x01 {
|
||||||
|
s.writeLock.Lock()
|
||||||
|
defer s.writeLock.Unlock()
|
||||||
|
return s.framer.WriteFrame(frame)
|
||||||
|
}
|
||||||
|
pingChan, pingOk := s.pingChans[frame.Id]
|
||||||
|
if pingOk {
|
||||||
|
close(pingChan)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Connection) handleGoAwayFrame(frame *spdy.GoAwayFrame) error {
|
||||||
|
debugMessage("(%p) Go away received", s)
|
||||||
|
s.receiveIdLock.Lock()
|
||||||
|
if s.goneAway {
|
||||||
|
s.receiveIdLock.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
s.goneAway = true
|
||||||
|
s.receiveIdLock.Unlock()
|
||||||
|
|
||||||
|
if s.lastStreamChan != nil {
|
||||||
|
stream, _ := s.getStream(frame.LastGoodStreamId)
|
||||||
|
go func() {
|
||||||
|
s.lastStreamChan <- stream
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not block frame handler waiting for closure
|
||||||
|
go s.shutdown(s.goAwayTimeout)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Connection) remoteStreamFinish(stream *Stream) {
|
||||||
|
stream.closeRemoteChannels()
|
||||||
|
|
||||||
|
stream.finishLock.Lock()
|
||||||
|
if stream.finished {
|
||||||
|
// Stream is fully closed, cleanup
|
||||||
|
s.removeStream(stream)
|
||||||
|
}
|
||||||
|
stream.finishLock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateStream creates a new spdy stream using the parameters for
|
||||||
|
// creating the stream frame. The stream frame will be sent upon
|
||||||
|
// calling this function, however this function does not wait for
|
||||||
|
// the reply frame. If waiting for the reply is desired, use
|
||||||
|
// the stream Wait or WaitTimeout function on the stream returned
|
||||||
|
// by this function.
|
||||||
|
func (s *Connection) CreateStream(headers http.Header, parent *Stream, fin bool) (*Stream, error) {
|
||||||
|
streamId := s.getNextStreamId()
|
||||||
|
if streamId == 0 {
|
||||||
|
return nil, fmt.Errorf("Unable to get new stream id")
|
||||||
|
}
|
||||||
|
|
||||||
|
stream := &Stream{
|
||||||
|
streamId: streamId,
|
||||||
|
parent: parent,
|
||||||
|
conn: s,
|
||||||
|
startChan: make(chan error),
|
||||||
|
headers: headers,
|
||||||
|
dataChan: make(chan []byte),
|
||||||
|
headerChan: make(chan http.Header),
|
||||||
|
closeChan: make(chan bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
debugMessage("(%p) (%p) Create stream", s, stream)
|
||||||
|
|
||||||
|
s.addStream(stream)
|
||||||
|
|
||||||
|
return stream, s.sendStream(stream, fin)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Connection) shutdown(closeTimeout time.Duration) {
|
||||||
|
// TODO Ensure this isn't called multiple times
|
||||||
|
s.shutdownLock.Lock()
|
||||||
|
if s.hasShutdown {
|
||||||
|
s.shutdownLock.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.hasShutdown = true
|
||||||
|
s.shutdownLock.Unlock()
|
||||||
|
|
||||||
|
var timeout <-chan time.Time
|
||||||
|
if closeTimeout > time.Duration(0) {
|
||||||
|
timeout = time.After(closeTimeout)
|
||||||
|
}
|
||||||
|
streamsClosed := make(chan bool)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
s.streamCond.L.Lock()
|
||||||
|
for len(s.streams) > 0 {
|
||||||
|
debugMessage("Streams opened: %d, %#v", len(s.streams), s.streams)
|
||||||
|
s.streamCond.Wait()
|
||||||
|
}
|
||||||
|
s.streamCond.L.Unlock()
|
||||||
|
close(streamsClosed)
|
||||||
|
}()
|
||||||
|
|
||||||
|
var err error
|
||||||
|
select {
|
||||||
|
case <-streamsClosed:
|
||||||
|
// No active streams, close should be safe
|
||||||
|
err = s.conn.Close()
|
||||||
|
case <-timeout:
|
||||||
|
// Force ungraceful close
|
||||||
|
err = s.conn.Close()
|
||||||
|
// Wait for cleanup to clear active streams
|
||||||
|
<-streamsClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
duration := 10 * time.Minute
|
||||||
|
time.AfterFunc(duration, func() {
|
||||||
|
select {
|
||||||
|
case err, ok := <-s.shutdownChan:
|
||||||
|
if ok {
|
||||||
|
fmt.Errorf("Unhandled close error after %s: %s", duration, err)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
})
|
||||||
|
s.shutdownChan <- err
|
||||||
|
}
|
||||||
|
close(s.shutdownChan)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Closes spdy connection by sending GoAway frame and initiating shutdown
|
||||||
|
func (s *Connection) Close() error {
|
||||||
|
s.receiveIdLock.Lock()
|
||||||
|
if s.goneAway {
|
||||||
|
s.receiveIdLock.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
s.goneAway = true
|
||||||
|
s.receiveIdLock.Unlock()
|
||||||
|
|
||||||
|
var lastStreamId spdy.StreamId
|
||||||
|
if s.receivedStreamId > 2 {
|
||||||
|
lastStreamId = s.receivedStreamId - 2
|
||||||
|
}
|
||||||
|
|
||||||
|
goAwayFrame := &spdy.GoAwayFrame{
|
||||||
|
LastGoodStreamId: lastStreamId,
|
||||||
|
Status: spdy.GoAwayOK,
|
||||||
|
}
|
||||||
|
|
||||||
|
s.writeLock.Lock()
|
||||||
|
err := s.framer.WriteFrame(goAwayFrame)
|
||||||
|
s.writeLock.Unlock()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
go s.shutdown(s.closeTimeout)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseWait closes the connection and waits for shutdown
|
||||||
|
// to finish. Note the underlying network Connection
|
||||||
|
// is not closed until the end of shutdown.
|
||||||
|
func (s *Connection) CloseWait() error {
|
||||||
|
closeErr := s.Close()
|
||||||
|
if closeErr != nil {
|
||||||
|
return closeErr
|
||||||
|
}
|
||||||
|
shutdownErr, ok := <-s.shutdownChan
|
||||||
|
if ok {
|
||||||
|
return shutdownErr
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait waits for the connection to finish shutdown or for
|
||||||
|
// the wait timeout duration to expire. This needs to be
|
||||||
|
// called either after Close has been called or the GOAWAYFRAME
|
||||||
|
// has been received. If the wait timeout is 0, this function
|
||||||
|
// will block until shutdown finishes. If wait is never called
|
||||||
|
// and a shutdown error occurs, that error will be logged as an
|
||||||
|
// unhandled error.
|
||||||
|
func (s *Connection) Wait(waitTimeout time.Duration) error {
|
||||||
|
var timeout <-chan time.Time
|
||||||
|
if waitTimeout > time.Duration(0) {
|
||||||
|
timeout = time.After(waitTimeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err, ok := <-s.shutdownChan:
|
||||||
|
if ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case <-timeout:
|
||||||
|
return ErrTimeout
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotifyClose registers a channel to be called when the remote
|
||||||
|
// peer inidicates connection closure. The last stream to be
|
||||||
|
// received by the remote will be sent on the channel. The notify
|
||||||
|
// timeout will determine the duration between go away received
|
||||||
|
// and the connection being closed.
|
||||||
|
func (s *Connection) NotifyClose(c chan<- *Stream, timeout time.Duration) {
|
||||||
|
s.goAwayTimeout = timeout
|
||||||
|
s.lastStreamChan = c
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCloseTimeout sets the amount of time close will wait for
|
||||||
|
// streams to finish before terminating the underlying network
|
||||||
|
// connection. Setting the timeout to 0 will cause close to
|
||||||
|
// wait forever, which is the default.
|
||||||
|
func (s *Connection) SetCloseTimeout(timeout time.Duration) {
|
||||||
|
s.closeTimeout = timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetIdleTimeout sets the amount of time the connection may sit idle before
|
||||||
|
// it is forcefully terminated.
|
||||||
|
func (s *Connection) SetIdleTimeout(timeout time.Duration) {
|
||||||
|
s.framer.setTimeoutChan <- timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Connection) sendHeaders(headers http.Header, stream *Stream, fin bool) error {
|
||||||
|
var flags spdy.ControlFlags
|
||||||
|
if fin {
|
||||||
|
flags = spdy.ControlFlagFin
|
||||||
|
}
|
||||||
|
|
||||||
|
headerFrame := &spdy.HeadersFrame{
|
||||||
|
StreamId: stream.streamId,
|
||||||
|
Headers: headers,
|
||||||
|
CFHeader: spdy.ControlFrameHeader{Flags: flags},
|
||||||
|
}
|
||||||
|
|
||||||
|
s.writeLock.Lock()
|
||||||
|
defer s.writeLock.Unlock()
|
||||||
|
return s.framer.WriteFrame(headerFrame)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Connection) sendReply(headers http.Header, stream *Stream, fin bool) error {
|
||||||
|
var flags spdy.ControlFlags
|
||||||
|
if fin {
|
||||||
|
flags = spdy.ControlFlagFin
|
||||||
|
}
|
||||||
|
|
||||||
|
replyFrame := &spdy.SynReplyFrame{
|
||||||
|
StreamId: stream.streamId,
|
||||||
|
Headers: headers,
|
||||||
|
CFHeader: spdy.ControlFrameHeader{Flags: flags},
|
||||||
|
}
|
||||||
|
|
||||||
|
s.writeLock.Lock()
|
||||||
|
defer s.writeLock.Unlock()
|
||||||
|
return s.framer.WriteFrame(replyFrame)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Connection) sendResetFrame(status spdy.RstStreamStatus, streamId spdy.StreamId) error {
|
||||||
|
resetFrame := &spdy.RstStreamFrame{
|
||||||
|
StreamId: streamId,
|
||||||
|
Status: status,
|
||||||
|
}
|
||||||
|
|
||||||
|
s.writeLock.Lock()
|
||||||
|
defer s.writeLock.Unlock()
|
||||||
|
return s.framer.WriteFrame(resetFrame)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Connection) sendReset(status spdy.RstStreamStatus, stream *Stream) error {
|
||||||
|
return s.sendResetFrame(status, stream.streamId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Connection) sendStream(stream *Stream, fin bool) error {
|
||||||
|
var flags spdy.ControlFlags
|
||||||
|
if fin {
|
||||||
|
flags = spdy.ControlFlagFin
|
||||||
|
stream.finished = true
|
||||||
|
}
|
||||||
|
|
||||||
|
var parentId spdy.StreamId
|
||||||
|
if stream.parent != nil {
|
||||||
|
parentId = stream.parent.streamId
|
||||||
|
}
|
||||||
|
|
||||||
|
streamFrame := &spdy.SynStreamFrame{
|
||||||
|
StreamId: spdy.StreamId(stream.streamId),
|
||||||
|
AssociatedToStreamId: spdy.StreamId(parentId),
|
||||||
|
Headers: stream.headers,
|
||||||
|
CFHeader: spdy.ControlFrameHeader{Flags: flags},
|
||||||
|
}
|
||||||
|
|
||||||
|
s.writeLock.Lock()
|
||||||
|
defer s.writeLock.Unlock()
|
||||||
|
return s.framer.WriteFrame(streamFrame)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getNextStreamId returns the next sequential id
|
||||||
|
// every call should produce a unique value or an error
|
||||||
|
func (s *Connection) getNextStreamId() spdy.StreamId {
|
||||||
|
s.nextIdLock.Lock()
|
||||||
|
defer s.nextIdLock.Unlock()
|
||||||
|
sid := s.nextStreamId
|
||||||
|
if sid > 0x7fffffff {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
s.nextStreamId = s.nextStreamId + 2
|
||||||
|
return sid
|
||||||
|
}
|
||||||
|
|
||||||
|
// PeekNextStreamId returns the next sequential id and keeps the next id untouched
|
||||||
|
func (s *Connection) PeekNextStreamId() spdy.StreamId {
|
||||||
|
sid := s.nextStreamId
|
||||||
|
return sid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Connection) validateStreamId(rid spdy.StreamId) error {
|
||||||
|
if rid > 0x7fffffff || rid < s.receivedStreamId {
|
||||||
|
return ErrInvalidStreamId
|
||||||
|
}
|
||||||
|
s.receivedStreamId = rid + 2
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Connection) addStream(stream *Stream) {
|
||||||
|
s.streamCond.L.Lock()
|
||||||
|
s.streams[stream.streamId] = stream
|
||||||
|
debugMessage("(%p) (%p) Stream added, broadcasting: %d", s, stream, stream.streamId)
|
||||||
|
s.streamCond.Broadcast()
|
||||||
|
s.streamCond.L.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Connection) removeStream(stream *Stream) {
|
||||||
|
s.streamCond.L.Lock()
|
||||||
|
delete(s.streams, stream.streamId)
|
||||||
|
debugMessage("Stream removed, broadcasting: %d", stream.streamId)
|
||||||
|
s.streamCond.Broadcast()
|
||||||
|
s.streamCond.L.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Connection) getStream(streamId spdy.StreamId) (stream *Stream, ok bool) {
|
||||||
|
s.streamLock.RLock()
|
||||||
|
stream, ok = s.streams[streamId]
|
||||||
|
s.streamLock.RUnlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindStream looks up the given stream id and either waits for the
|
||||||
|
// stream to be found or returns nil if the stream id is no longer
|
||||||
|
// valid.
|
||||||
|
func (s *Connection) FindStream(streamId uint32) *Stream {
|
||||||
|
var stream *Stream
|
||||||
|
var ok bool
|
||||||
|
s.streamCond.L.Lock()
|
||||||
|
stream, ok = s.streams[spdy.StreamId(streamId)]
|
||||||
|
debugMessage("(%p) Found stream %d? %t", s, spdy.StreamId(streamId), ok)
|
||||||
|
for !ok && streamId >= uint32(s.receivedStreamId) {
|
||||||
|
s.streamCond.Wait()
|
||||||
|
stream, ok = s.streams[spdy.StreamId(streamId)]
|
||||||
|
}
|
||||||
|
s.streamCond.L.Unlock()
|
||||||
|
return stream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Connection) CloseChan() <-chan bool {
|
||||||
|
return s.closeChan
|
||||||
|
}
|
38
Godeps/_workspace/src/github.com/docker/spdystream/handlers.go
generated
vendored
Normal file
38
Godeps/_workspace/src/github.com/docker/spdystream/handlers.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package spdystream
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MirrorStreamHandler mirrors all streams.
|
||||||
|
func MirrorStreamHandler(stream *Stream) {
|
||||||
|
replyErr := stream.SendReply(http.Header{}, false)
|
||||||
|
if replyErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
io.Copy(stream, stream)
|
||||||
|
stream.Close()
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
header, receiveErr := stream.ReceiveHeader()
|
||||||
|
if receiveErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sendErr := stream.SendHeader(header, false)
|
||||||
|
if sendErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NoopStreamHandler does nothing when stream connects, most
|
||||||
|
// likely used with RejectAuthHandler which will not allow any
|
||||||
|
// streams to make it to the stream handler.
|
||||||
|
func NoOpStreamHandler(stream *Stream) {
|
||||||
|
stream.SendReply(http.Header{}, false)
|
||||||
|
}
|
97
Godeps/_workspace/src/github.com/docker/spdystream/priority.go
generated
vendored
Normal file
97
Godeps/_workspace/src/github.com/docker/spdystream/priority.go
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
package spdystream
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.google.com/p/go.net/spdy"
|
||||||
|
"container/heap"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type prioritizedFrame struct {
|
||||||
|
frame spdy.Frame
|
||||||
|
priority uint8
|
||||||
|
insertId uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type frameQueue []*prioritizedFrame
|
||||||
|
|
||||||
|
func (fq frameQueue) Len() int {
|
||||||
|
return len(fq)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fq frameQueue) Less(i, j int) bool {
|
||||||
|
if fq[i].priority == fq[j].priority {
|
||||||
|
return fq[i].insertId < fq[j].insertId
|
||||||
|
}
|
||||||
|
return fq[i].priority < fq[j].priority
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fq frameQueue) Swap(i, j int) {
|
||||||
|
fq[i], fq[j] = fq[j], fq[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fq *frameQueue) Push(x interface{}) {
|
||||||
|
*fq = append(*fq, x.(*prioritizedFrame))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fq *frameQueue) Pop() interface{} {
|
||||||
|
old := *fq
|
||||||
|
n := len(old)
|
||||||
|
*fq = old[0 : n-1]
|
||||||
|
return old[n-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
type PriorityFrameQueue struct {
|
||||||
|
queue *frameQueue
|
||||||
|
c *sync.Cond
|
||||||
|
size int
|
||||||
|
nextInsertId uint64
|
||||||
|
drain bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPriorityFrameQueue(size int) *PriorityFrameQueue {
|
||||||
|
queue := make(frameQueue, 0, size)
|
||||||
|
heap.Init(&queue)
|
||||||
|
|
||||||
|
return &PriorityFrameQueue{
|
||||||
|
queue: &queue,
|
||||||
|
size: size,
|
||||||
|
c: sync.NewCond(&sync.Mutex{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *PriorityFrameQueue) Push(frame spdy.Frame, priority uint8) {
|
||||||
|
q.c.L.Lock()
|
||||||
|
defer q.c.L.Unlock()
|
||||||
|
for q.queue.Len() >= q.size {
|
||||||
|
q.c.Wait()
|
||||||
|
}
|
||||||
|
pFrame := &prioritizedFrame{
|
||||||
|
frame: frame,
|
||||||
|
priority: priority,
|
||||||
|
insertId: q.nextInsertId,
|
||||||
|
}
|
||||||
|
q.nextInsertId = q.nextInsertId + 1
|
||||||
|
heap.Push(q.queue, pFrame)
|
||||||
|
q.c.Signal()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *PriorityFrameQueue) Pop() spdy.Frame {
|
||||||
|
q.c.L.Lock()
|
||||||
|
defer q.c.L.Unlock()
|
||||||
|
for q.queue.Len() == 0 {
|
||||||
|
if q.drain {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
q.c.Wait()
|
||||||
|
}
|
||||||
|
frame := heap.Pop(q.queue).(*prioritizedFrame).frame
|
||||||
|
q.c.Signal()
|
||||||
|
return frame
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *PriorityFrameQueue) Drain() {
|
||||||
|
q.c.L.Lock()
|
||||||
|
defer q.c.L.Unlock()
|
||||||
|
q.drain = true
|
||||||
|
q.c.Broadcast()
|
||||||
|
}
|
107
Godeps/_workspace/src/github.com/docker/spdystream/priority_test.go
generated
vendored
Normal file
107
Godeps/_workspace/src/github.com/docker/spdystream/priority_test.go
generated
vendored
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
package spdystream
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.google.com/p/go.net/spdy"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPriorityQueueOrdering(t *testing.T) {
|
||||||
|
queue := NewPriorityFrameQueue(150)
|
||||||
|
data1 := &spdy.DataFrame{}
|
||||||
|
data2 := &spdy.DataFrame{}
|
||||||
|
data3 := &spdy.DataFrame{}
|
||||||
|
data4 := &spdy.DataFrame{}
|
||||||
|
queue.Push(data1, 2)
|
||||||
|
queue.Push(data2, 1)
|
||||||
|
queue.Push(data3, 1)
|
||||||
|
queue.Push(data4, 0)
|
||||||
|
|
||||||
|
if queue.Pop() != data4 {
|
||||||
|
t.Fatalf("Wrong order, expected data4 first")
|
||||||
|
}
|
||||||
|
if queue.Pop() != data2 {
|
||||||
|
t.Fatalf("Wrong order, expected data2 second")
|
||||||
|
}
|
||||||
|
if queue.Pop() != data3 {
|
||||||
|
t.Fatalf("Wrong order, expected data3 third")
|
||||||
|
}
|
||||||
|
if queue.Pop() != data1 {
|
||||||
|
t.Fatalf("Wrong order, expected data1 fourth")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert 50 Medium priority frames
|
||||||
|
for i := spdy.StreamId(50); i < 100; i++ {
|
||||||
|
queue.Push(&spdy.DataFrame{StreamId: i}, 1)
|
||||||
|
}
|
||||||
|
// Insert 50 low priority frames
|
||||||
|
for i := spdy.StreamId(100); i < 150; i++ {
|
||||||
|
queue.Push(&spdy.DataFrame{StreamId: i}, 2)
|
||||||
|
}
|
||||||
|
// Insert 50 high priority frames
|
||||||
|
for i := spdy.StreamId(0); i < 50; i++ {
|
||||||
|
queue.Push(&spdy.DataFrame{StreamId: i}, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := spdy.StreamId(0); i < 150; i++ {
|
||||||
|
frame := queue.Pop()
|
||||||
|
if frame.(*spdy.DataFrame).StreamId != i {
|
||||||
|
t.Fatalf("Wrong frame\nActual: %d\nExpecting: %d", frame.(*spdy.DataFrame).StreamId, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPriorityQueueSync(t *testing.T) {
|
||||||
|
queue := NewPriorityFrameQueue(150)
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
insertRange := func(start, stop spdy.StreamId, priority uint8) {
|
||||||
|
for i := start; i < stop; i++ {
|
||||||
|
queue.Push(&spdy.DataFrame{StreamId: i}, priority)
|
||||||
|
}
|
||||||
|
wg.Done()
|
||||||
|
}
|
||||||
|
wg.Add(3)
|
||||||
|
go insertRange(spdy.StreamId(100), spdy.StreamId(150), 2)
|
||||||
|
go insertRange(spdy.StreamId(0), spdy.StreamId(50), 0)
|
||||||
|
go insertRange(spdy.StreamId(50), spdy.StreamId(100), 1)
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
for i := spdy.StreamId(0); i < 150; i++ {
|
||||||
|
frame := queue.Pop()
|
||||||
|
if frame.(*spdy.DataFrame).StreamId != i {
|
||||||
|
t.Fatalf("Wrong frame\nActual: %d\nExpecting: %d", frame.(*spdy.DataFrame).StreamId, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPriorityQueueBlocking(t *testing.T) {
|
||||||
|
queue := NewPriorityFrameQueue(15)
|
||||||
|
for i := 0; i < 15; i++ {
|
||||||
|
queue.Push(&spdy.DataFrame{}, 2)
|
||||||
|
}
|
||||||
|
doneChan := make(chan bool)
|
||||||
|
go func() {
|
||||||
|
queue.Push(&spdy.DataFrame{}, 2)
|
||||||
|
close(doneChan)
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case <-doneChan:
|
||||||
|
t.Fatalf("Push succeeded, expected to block")
|
||||||
|
case <-time.After(time.Millisecond):
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.Pop()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-doneChan:
|
||||||
|
break
|
||||||
|
case <-time.After(time.Millisecond):
|
||||||
|
t.Fatalf("Push should have succeeded, but timeout reached")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 15; i++ {
|
||||||
|
queue.Pop()
|
||||||
|
}
|
||||||
|
}
|
113
Godeps/_workspace/src/github.com/docker/spdystream/spdy_bench_test.go
generated
vendored
Normal file
113
Godeps/_workspace/src/github.com/docker/spdystream/spdy_bench_test.go
generated
vendored
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
package spdystream
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func configureServer() (io.Closer, string, *sync.WaitGroup) {
|
||||||
|
authenticated = true
|
||||||
|
wg := &sync.WaitGroup{}
|
||||||
|
server, listen, serverErr := runServer(wg)
|
||||||
|
|
||||||
|
if serverErr != nil {
|
||||||
|
panic(serverErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return server, listen, wg
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDial10000(b *testing.B) {
|
||||||
|
server, addr, wg := configureServer()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
server.Close()
|
||||||
|
wg.Wait()
|
||||||
|
}()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
conn, dialErr := net.Dial("tcp", addr)
|
||||||
|
if dialErr != nil {
|
||||||
|
panic(fmt.Sprintf("Error dialing server: %s", dialErr))
|
||||||
|
}
|
||||||
|
conn.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDialWithSPDYStream10000(b *testing.B) {
|
||||||
|
server, addr, wg := configureServer()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
server.Close()
|
||||||
|
wg.Wait()
|
||||||
|
}()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
conn, dialErr := net.Dial("tcp", addr)
|
||||||
|
if dialErr != nil {
|
||||||
|
b.Fatalf("Error dialing server: %s", dialErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
spdyConn, spdyErr := NewConnection(conn, false)
|
||||||
|
if spdyErr != nil {
|
||||||
|
b.Fatalf("Error creating spdy connection: %s", spdyErr)
|
||||||
|
}
|
||||||
|
go spdyConn.Serve(NoOpStreamHandler)
|
||||||
|
|
||||||
|
closeErr := spdyConn.Close()
|
||||||
|
if closeErr != nil {
|
||||||
|
b.Fatalf("Error closing connection: %s, closeErr")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func benchmarkStreamWithDataAndSize(size uint64, b *testing.B) {
|
||||||
|
server, addr, wg := configureServer()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
server.Close()
|
||||||
|
wg.Wait()
|
||||||
|
}()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
conn, dialErr := net.Dial("tcp", addr)
|
||||||
|
if dialErr != nil {
|
||||||
|
b.Fatalf("Error dialing server: %s", dialErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
spdyConn, spdyErr := NewConnection(conn, false)
|
||||||
|
if spdyErr != nil {
|
||||||
|
b.Fatalf("Error creating spdy connection: %s", spdyErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
go spdyConn.Serve(MirrorStreamHandler)
|
||||||
|
|
||||||
|
stream, err := spdyConn.CreateStream(http.Header{}, nil, false)
|
||||||
|
|
||||||
|
writer := make([]byte, size)
|
||||||
|
|
||||||
|
stream.Write(writer)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
reader := make([]byte, size)
|
||||||
|
stream.Read(reader)
|
||||||
|
|
||||||
|
stream.Close()
|
||||||
|
|
||||||
|
closeErr := spdyConn.Close()
|
||||||
|
if closeErr != nil {
|
||||||
|
b.Fatalf("Error closing connection: %s, closeErr")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkStreamWith1Byte10000(b *testing.B) { benchmarkStreamWithDataAndSize(1, b) }
|
||||||
|
func BenchmarkStreamWith1KiloByte10000(b *testing.B) { benchmarkStreamWithDataAndSize(1024, b) }
|
||||||
|
func BenchmarkStreamWith1Megabyte10000(b *testing.B) { benchmarkStreamWithDataAndSize(1024*1024, b) }
|
735
Godeps/_workspace/src/github.com/docker/spdystream/spdy_test.go
generated
vendored
Normal file
735
Godeps/_workspace/src/github.com/docker/spdystream/spdy_test.go
generated
vendored
Normal file
@@ -0,0 +1,735 @@
|
|||||||
|
package spdystream
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSpdyStreams(t *testing.T) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
server, listen, serverErr := runServer(&wg)
|
||||||
|
if serverErr != nil {
|
||||||
|
t.Fatalf("Error initializing server: %s", serverErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, dialErr := net.Dial("tcp", listen)
|
||||||
|
if dialErr != nil {
|
||||||
|
t.Fatalf("Error dialing server: %s", dialErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
spdyConn, spdyErr := NewConnection(conn, false)
|
||||||
|
if spdyErr != nil {
|
||||||
|
t.Fatalf("Error creating spdy connection: %s", spdyErr)
|
||||||
|
}
|
||||||
|
go spdyConn.Serve(NoOpStreamHandler)
|
||||||
|
|
||||||
|
authenticated = true
|
||||||
|
stream, streamErr := spdyConn.CreateStream(http.Header{}, nil, false)
|
||||||
|
if streamErr != nil {
|
||||||
|
t.Fatalf("Error creating stream: %s", streamErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
waitErr := stream.Wait()
|
||||||
|
if waitErr != nil {
|
||||||
|
t.Fatalf("Error waiting for stream: %s", waitErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
message := []byte("hello")
|
||||||
|
writeErr := stream.WriteData(message, false)
|
||||||
|
if writeErr != nil {
|
||||||
|
t.Fatalf("Error writing data")
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, 10)
|
||||||
|
n, readErr := stream.Read(buf)
|
||||||
|
if readErr != nil {
|
||||||
|
t.Fatalf("Error reading data from stream: %s", readErr)
|
||||||
|
}
|
||||||
|
if n != 5 {
|
||||||
|
t.Fatalf("Unexpected number of bytes read:\nActual: %d\nExpected: 5", n)
|
||||||
|
}
|
||||||
|
if bytes.Compare(buf[:n], message) != 0 {
|
||||||
|
t.Fatalf("Did not receive expected message:\nActual: %s\nExpectd: %s", buf, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
headers := http.Header{
|
||||||
|
"TestKey": []string{"TestVal"},
|
||||||
|
}
|
||||||
|
sendErr := stream.SendHeader(headers, false)
|
||||||
|
if sendErr != nil {
|
||||||
|
t.Fatalf("Error sending headers: %s", sendErr)
|
||||||
|
}
|
||||||
|
receiveHeaders, receiveErr := stream.ReceiveHeader()
|
||||||
|
if receiveErr != nil {
|
||||||
|
t.Fatalf("Error receiving headers: %s", receiveErr)
|
||||||
|
}
|
||||||
|
if len(receiveHeaders) != 1 {
|
||||||
|
t.Fatalf("Unexpected number of headers:\nActual: %d\nExpecting:%d", len(receiveHeaders), 1)
|
||||||
|
}
|
||||||
|
testVal := receiveHeaders.Get("TestKey")
|
||||||
|
if testVal != "TestVal" {
|
||||||
|
t.Fatalf("Wrong test value:\nActual: %q\nExpecting: %q", testVal, "TestVal")
|
||||||
|
}
|
||||||
|
|
||||||
|
writeErr = stream.WriteData(message, true)
|
||||||
|
if writeErr != nil {
|
||||||
|
t.Fatalf("Error writing data")
|
||||||
|
}
|
||||||
|
|
||||||
|
smallBuf := make([]byte, 3)
|
||||||
|
n, readErr = stream.Read(smallBuf)
|
||||||
|
if readErr != nil {
|
||||||
|
t.Fatalf("Error reading data from stream: %s", readErr)
|
||||||
|
}
|
||||||
|
if n != 3 {
|
||||||
|
t.Fatalf("Unexpected number of bytes read:\nActual: %d\nExpected: 3", n)
|
||||||
|
}
|
||||||
|
if bytes.Compare(smallBuf[:n], []byte("hel")) != 0 {
|
||||||
|
t.Fatalf("Did not receive expected message:\nActual: %s\nExpectd: %s", smallBuf[:n], message)
|
||||||
|
}
|
||||||
|
n, readErr = stream.Read(smallBuf)
|
||||||
|
if readErr != nil {
|
||||||
|
t.Fatalf("Error reading data from stream: %s", readErr)
|
||||||
|
}
|
||||||
|
if n != 2 {
|
||||||
|
t.Fatalf("Unexpected number of bytes read:\nActual: %d\nExpected: 2", n)
|
||||||
|
}
|
||||||
|
if bytes.Compare(smallBuf[:n], []byte("lo")) != 0 {
|
||||||
|
t.Fatalf("Did not receive expected message:\nActual: %s\nExpected: lo", smallBuf[:n])
|
||||||
|
}
|
||||||
|
|
||||||
|
n, readErr = stream.Read(buf)
|
||||||
|
if readErr != io.EOF {
|
||||||
|
t.Fatalf("Expected EOF reading from finished stream, read %d bytes", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Closing again should return error since stream is already closed
|
||||||
|
streamCloseErr := stream.Close()
|
||||||
|
if streamCloseErr == nil {
|
||||||
|
t.Fatalf("No error closing finished stream")
|
||||||
|
}
|
||||||
|
if streamCloseErr != ErrWriteClosedStream {
|
||||||
|
t.Fatalf("Unexpected error closing stream: %s", streamCloseErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
streamResetErr := stream.Reset()
|
||||||
|
if streamResetErr != nil {
|
||||||
|
t.Fatalf("Error reseting stream: %s", streamResetErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticated = false
|
||||||
|
badStream, badStreamErr := spdyConn.CreateStream(http.Header{}, nil, false)
|
||||||
|
if badStreamErr != nil {
|
||||||
|
t.Fatalf("Error creating stream: %s", badStreamErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
waitErr = badStream.Wait()
|
||||||
|
if waitErr == nil {
|
||||||
|
t.Fatalf("Did not receive error creating stream")
|
||||||
|
}
|
||||||
|
if waitErr != ErrReset {
|
||||||
|
t.Fatalf("Unexpected error creating stream: %s", waitErr)
|
||||||
|
}
|
||||||
|
streamCloseErr = badStream.Close()
|
||||||
|
if streamCloseErr == nil {
|
||||||
|
t.Fatalf("No error closing bad stream")
|
||||||
|
}
|
||||||
|
|
||||||
|
spdyCloseErr := spdyConn.Close()
|
||||||
|
if spdyCloseErr != nil {
|
||||||
|
t.Fatalf("Error closing spdy connection: %s", spdyCloseErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
closeErr := server.Close()
|
||||||
|
if closeErr != nil {
|
||||||
|
t.Fatalf("Error shutting down server: %s", closeErr)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPing(t *testing.T) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
server, listen, serverErr := runServer(&wg)
|
||||||
|
if serverErr != nil {
|
||||||
|
t.Fatalf("Error initializing server: %s", serverErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, dialErr := net.Dial("tcp", listen)
|
||||||
|
if dialErr != nil {
|
||||||
|
t.Fatalf("Error dialing server: %s", dialErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
spdyConn, spdyErr := NewConnection(conn, false)
|
||||||
|
if spdyErr != nil {
|
||||||
|
t.Fatalf("Error creating spdy connection: %s", spdyErr)
|
||||||
|
}
|
||||||
|
go spdyConn.Serve(NoOpStreamHandler)
|
||||||
|
|
||||||
|
pingTime, pingErr := spdyConn.Ping()
|
||||||
|
if pingErr != nil {
|
||||||
|
t.Fatalf("Error pinging server: %s", pingErr)
|
||||||
|
}
|
||||||
|
if pingTime == time.Duration(0) {
|
||||||
|
t.Fatalf("Expecting non-zero ping time")
|
||||||
|
}
|
||||||
|
|
||||||
|
closeErr := server.Close()
|
||||||
|
if closeErr != nil {
|
||||||
|
t.Fatalf("Error shutting down server: %s", closeErr)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHalfClose(t *testing.T) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
server, listen, serverErr := runServer(&wg)
|
||||||
|
if serverErr != nil {
|
||||||
|
t.Fatalf("Error initializing server: %s", serverErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, dialErr := net.Dial("tcp", listen)
|
||||||
|
if dialErr != nil {
|
||||||
|
t.Fatalf("Error dialing server: %s", dialErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
spdyConn, spdyErr := NewConnection(conn, false)
|
||||||
|
if spdyErr != nil {
|
||||||
|
t.Fatalf("Error creating spdy connection: %s", spdyErr)
|
||||||
|
}
|
||||||
|
go spdyConn.Serve(NoOpStreamHandler)
|
||||||
|
|
||||||
|
authenticated = true
|
||||||
|
stream, streamErr := spdyConn.CreateStream(http.Header{}, nil, false)
|
||||||
|
if streamErr != nil {
|
||||||
|
t.Fatalf("Error creating stream: %s", streamErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
waitErr := stream.Wait()
|
||||||
|
if waitErr != nil {
|
||||||
|
t.Fatalf("Error waiting for stream: %s", waitErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
message := []byte("hello and will read after close")
|
||||||
|
writeErr := stream.WriteData(message, false)
|
||||||
|
if writeErr != nil {
|
||||||
|
t.Fatalf("Error writing data")
|
||||||
|
}
|
||||||
|
|
||||||
|
streamCloseErr := stream.Close()
|
||||||
|
if streamCloseErr != nil {
|
||||||
|
t.Fatalf("Error closing stream: %s", streamCloseErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, 40)
|
||||||
|
n, readErr := stream.Read(buf)
|
||||||
|
if readErr != nil {
|
||||||
|
t.Fatalf("Error reading data from stream: %s", readErr)
|
||||||
|
}
|
||||||
|
if n != 31 {
|
||||||
|
t.Fatalf("Unexpected number of bytes read:\nActual: %d\nExpected: 5", n)
|
||||||
|
}
|
||||||
|
if bytes.Compare(buf[:n], message) != 0 {
|
||||||
|
t.Fatalf("Did not receive expected message:\nActual: %s\nExpectd: %s", buf, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
spdyCloseErr := spdyConn.Close()
|
||||||
|
if spdyCloseErr != nil {
|
||||||
|
t.Fatalf("Error closing spdy connection: %s", spdyCloseErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
closeErr := server.Close()
|
||||||
|
if closeErr != nil {
|
||||||
|
t.Fatalf("Error shutting down server: %s", closeErr)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnexpectedRemoteConnectionClosed(t *testing.T) {
|
||||||
|
tt := []struct {
|
||||||
|
closeReceiver bool
|
||||||
|
closeSender bool
|
||||||
|
}{
|
||||||
|
{closeReceiver: true, closeSender: false},
|
||||||
|
{closeReceiver: false, closeSender: true},
|
||||||
|
{closeReceiver: false, closeSender: false},
|
||||||
|
}
|
||||||
|
for tix, tc := range tt {
|
||||||
|
listener, listenErr := net.Listen("tcp", "localhost:0")
|
||||||
|
if listenErr != nil {
|
||||||
|
t.Fatalf("Error listening: %v", listenErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
var serverConn net.Conn
|
||||||
|
var connErr error
|
||||||
|
go func() {
|
||||||
|
serverConn, connErr = listener.Accept()
|
||||||
|
if connErr != nil {
|
||||||
|
t.Fatalf("Error accepting: %v", connErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
serverSpdyConn, _ := NewConnection(serverConn, true)
|
||||||
|
go serverSpdyConn.Serve(func(stream *Stream) {
|
||||||
|
stream.SendReply(http.Header{}, tc.closeSender)
|
||||||
|
})
|
||||||
|
}()
|
||||||
|
|
||||||
|
conn, dialErr := net.Dial("tcp", listener.Addr().String())
|
||||||
|
if dialErr != nil {
|
||||||
|
t.Fatalf("Error dialing server: %s", dialErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
spdyConn, spdyErr := NewConnection(conn, false)
|
||||||
|
if spdyErr != nil {
|
||||||
|
t.Fatalf("Error creating spdy connection: %s", spdyErr)
|
||||||
|
}
|
||||||
|
go spdyConn.Serve(NoOpStreamHandler)
|
||||||
|
|
||||||
|
authenticated = true
|
||||||
|
stream, streamErr := spdyConn.CreateStream(http.Header{}, nil, false)
|
||||||
|
if streamErr != nil {
|
||||||
|
t.Fatalf("Error creating stream: %s", streamErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
waitErr := stream.Wait()
|
||||||
|
if waitErr != nil {
|
||||||
|
t.Fatalf("Error waiting for stream: %s", waitErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.closeReceiver {
|
||||||
|
// make stream half closed, receive only
|
||||||
|
stream.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
streamch := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
b := make([]byte, 1)
|
||||||
|
_, err := stream.Read(b)
|
||||||
|
streamch <- err
|
||||||
|
}()
|
||||||
|
|
||||||
|
closeErr := serverConn.Close()
|
||||||
|
if closeErr != nil {
|
||||||
|
t.Fatalf("Error shutting down server: %s", closeErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case e := <-streamch:
|
||||||
|
if e == nil || e != io.EOF {
|
||||||
|
t.Fatalf("(%d) Expected to get an EOF stream error", tix)
|
||||||
|
}
|
||||||
|
case <-time.After(500 * time.Millisecond):
|
||||||
|
t.Fatalf("(%d) Timeout waiting for stream closure", tix)
|
||||||
|
}
|
||||||
|
|
||||||
|
closeErr = conn.Close()
|
||||||
|
if closeErr != nil {
|
||||||
|
t.Fatalf("Error closing client connection: %s", closeErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
listenErr = listener.Close()
|
||||||
|
if listenErr != nil {
|
||||||
|
t.Fatalf("Error closing listener: %s", listenErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloseNotification(t *testing.T) {
|
||||||
|
listener, listenErr := net.Listen("tcp", "localhost:0")
|
||||||
|
if listenErr != nil {
|
||||||
|
t.Fatalf("Error listening: %v", listenErr)
|
||||||
|
}
|
||||||
|
listen := listener.Addr().String()
|
||||||
|
|
||||||
|
serverConnChan := make(chan net.Conn)
|
||||||
|
go func() {
|
||||||
|
serverConn, err := listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error accepting: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
serverSpdyConn, err := NewConnection(serverConn, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error creating server connection: %v", err)
|
||||||
|
}
|
||||||
|
go serverSpdyConn.Serve(NoOpStreamHandler)
|
||||||
|
<-serverSpdyConn.CloseChan()
|
||||||
|
serverConnChan <- serverConn
|
||||||
|
}()
|
||||||
|
|
||||||
|
conn, dialErr := net.Dial("tcp", listen)
|
||||||
|
if dialErr != nil {
|
||||||
|
t.Fatalf("Error dialing server: %s", dialErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
spdyConn, spdyErr := NewConnection(conn, false)
|
||||||
|
if spdyErr != nil {
|
||||||
|
t.Fatalf("Error creating spdy connection: %s", spdyErr)
|
||||||
|
}
|
||||||
|
go spdyConn.Serve(NoOpStreamHandler)
|
||||||
|
|
||||||
|
// close client conn
|
||||||
|
err := conn.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error closing client connection: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var serverConn net.Conn
|
||||||
|
select {
|
||||||
|
case serverConn = <-serverConnChan:
|
||||||
|
case <-time.After(500 * time.Millisecond):
|
||||||
|
t.Fatal("Timed out waiting for connection closed notification")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = serverConn.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error closing serverConn: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
listenErr = listener.Close()
|
||||||
|
if listenErr != nil {
|
||||||
|
t.Fatalf("Error closing listener: %s", listenErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIdleNoTimeoutSet(t *testing.T) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
server, listen, serverErr := runServer(&wg)
|
||||||
|
if serverErr != nil {
|
||||||
|
t.Fatalf("Error initializing server: %s", serverErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, dialErr := net.Dial("tcp", listen)
|
||||||
|
if dialErr != nil {
|
||||||
|
t.Fatalf("Error dialing server: %s", dialErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
spdyConn, spdyErr := NewConnection(conn, false)
|
||||||
|
if spdyErr != nil {
|
||||||
|
t.Fatalf("Error creating spdy connection: %s", spdyErr)
|
||||||
|
}
|
||||||
|
go spdyConn.Serve(NoOpStreamHandler)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-spdyConn.CloseChan():
|
||||||
|
t.Fatal("Unexpected connection closure")
|
||||||
|
case <-time.After(10 * time.Millisecond):
|
||||||
|
}
|
||||||
|
|
||||||
|
closeErr := server.Close()
|
||||||
|
if closeErr != nil {
|
||||||
|
t.Fatalf("Error shutting down server: %s", closeErr)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIdleClearTimeout(t *testing.T) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
server, listen, serverErr := runServer(&wg)
|
||||||
|
if serverErr != nil {
|
||||||
|
t.Fatalf("Error initializing server: %s", serverErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, dialErr := net.Dial("tcp", listen)
|
||||||
|
if dialErr != nil {
|
||||||
|
t.Fatalf("Error dialing server: %s", dialErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
spdyConn, spdyErr := NewConnection(conn, false)
|
||||||
|
if spdyErr != nil {
|
||||||
|
t.Fatalf("Error creating spdy connection: %s", spdyErr)
|
||||||
|
}
|
||||||
|
go spdyConn.Serve(NoOpStreamHandler)
|
||||||
|
|
||||||
|
spdyConn.SetIdleTimeout(10 * time.Millisecond)
|
||||||
|
spdyConn.SetIdleTimeout(0)
|
||||||
|
select {
|
||||||
|
case <-spdyConn.CloseChan():
|
||||||
|
t.Fatal("Unexpected connection closure")
|
||||||
|
case <-time.After(20 * time.Millisecond):
|
||||||
|
}
|
||||||
|
|
||||||
|
closeErr := server.Close()
|
||||||
|
if closeErr != nil {
|
||||||
|
t.Fatalf("Error shutting down server: %s", closeErr)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIdleNoData(t *testing.T) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
server, listen, serverErr := runServer(&wg)
|
||||||
|
if serverErr != nil {
|
||||||
|
t.Fatalf("Error initializing server: %s", serverErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, dialErr := net.Dial("tcp", listen)
|
||||||
|
if dialErr != nil {
|
||||||
|
t.Fatalf("Error dialing server: %s", dialErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
spdyConn, spdyErr := NewConnection(conn, false)
|
||||||
|
if spdyErr != nil {
|
||||||
|
t.Fatalf("Error creating spdy connection: %s", spdyErr)
|
||||||
|
}
|
||||||
|
go spdyConn.Serve(NoOpStreamHandler)
|
||||||
|
|
||||||
|
spdyConn.SetIdleTimeout(10 * time.Millisecond)
|
||||||
|
select {
|
||||||
|
case <-spdyConn.CloseChan():
|
||||||
|
case <-time.After(20 * time.Millisecond):
|
||||||
|
t.Fatal("Timed out waiting for idle connection closure")
|
||||||
|
}
|
||||||
|
|
||||||
|
closeErr := server.Close()
|
||||||
|
if closeErr != nil {
|
||||||
|
t.Fatalf("Error shutting down server: %s", closeErr)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIdleWithData(t *testing.T) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
server, listen, serverErr := runServer(&wg)
|
||||||
|
if serverErr != nil {
|
||||||
|
t.Fatalf("Error initializing server: %s", serverErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, dialErr := net.Dial("tcp", listen)
|
||||||
|
if dialErr != nil {
|
||||||
|
t.Fatalf("Error dialing server: %s", dialErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
spdyConn, spdyErr := NewConnection(conn, false)
|
||||||
|
if spdyErr != nil {
|
||||||
|
t.Fatalf("Error creating spdy connection: %s", spdyErr)
|
||||||
|
}
|
||||||
|
go spdyConn.Serve(NoOpStreamHandler)
|
||||||
|
|
||||||
|
spdyConn.SetIdleTimeout(25 * time.Millisecond)
|
||||||
|
|
||||||
|
authenticated = true
|
||||||
|
stream, err := spdyConn.CreateStream(http.Header{}, nil, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error creating stream: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
writeCh := make(chan struct{})
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
b := []byte{1, 2, 3, 4, 5}
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
_, err = stream.Write(b)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error writing to stream: %v", err)
|
||||||
|
}
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
}
|
||||||
|
close(writeCh)
|
||||||
|
}()
|
||||||
|
|
||||||
|
writesFinished := false
|
||||||
|
|
||||||
|
expired := time.NewTimer(200 * time.Millisecond)
|
||||||
|
|
||||||
|
Loop:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-writeCh:
|
||||||
|
writesFinished = true
|
||||||
|
case <-spdyConn.CloseChan():
|
||||||
|
if !writesFinished {
|
||||||
|
t.Fatal("Connection closed before all writes finished")
|
||||||
|
}
|
||||||
|
break Loop
|
||||||
|
case <-expired.C:
|
||||||
|
t.Fatal("Timed out waiting for idle connection closure")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closeErr := server.Close()
|
||||||
|
if closeErr != nil {
|
||||||
|
t.Fatalf("Error shutting down server: %s", closeErr)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHalfClosedIdleTimeout(t *testing.T) {
|
||||||
|
listener, listenErr := net.Listen("tcp", "localhost:0")
|
||||||
|
if listenErr != nil {
|
||||||
|
t.Fatalf("Error listening: %v", listenErr)
|
||||||
|
}
|
||||||
|
listen := listener.Addr().String()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
serverConn, err := listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error accepting: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
serverSpdyConn, err := NewConnection(serverConn, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error creating server connection: %v", err)
|
||||||
|
}
|
||||||
|
go serverSpdyConn.Serve(func(s *Stream) {
|
||||||
|
s.SendReply(http.Header{}, true)
|
||||||
|
})
|
||||||
|
serverSpdyConn.SetIdleTimeout(10 * time.Millisecond)
|
||||||
|
}()
|
||||||
|
|
||||||
|
conn, dialErr := net.Dial("tcp", listen)
|
||||||
|
if dialErr != nil {
|
||||||
|
t.Fatalf("Error dialing server: %s", dialErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
spdyConn, spdyErr := NewConnection(conn, false)
|
||||||
|
if spdyErr != nil {
|
||||||
|
t.Fatalf("Error creating spdy connection: %s", spdyErr)
|
||||||
|
}
|
||||||
|
go spdyConn.Serve(NoOpStreamHandler)
|
||||||
|
|
||||||
|
stream, err := spdyConn.CreateStream(http.Header{}, nil, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error creating stream: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(20 * time.Millisecond)
|
||||||
|
|
||||||
|
stream.Reset()
|
||||||
|
|
||||||
|
err = spdyConn.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error closing client spdy conn: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStreamReset(t *testing.T) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
server, listen, serverErr := runServer(&wg)
|
||||||
|
if serverErr != nil {
|
||||||
|
t.Fatalf("Error initializing server: %s", serverErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, dialErr := net.Dial("tcp", listen)
|
||||||
|
if dialErr != nil {
|
||||||
|
t.Fatalf("Error dialing server: %s", dialErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
spdyConn, spdyErr := NewConnection(conn, false)
|
||||||
|
if spdyErr != nil {
|
||||||
|
t.Fatalf("Error creating spdy connection: %s", spdyErr)
|
||||||
|
}
|
||||||
|
go spdyConn.Serve(NoOpStreamHandler)
|
||||||
|
|
||||||
|
authenticated = true
|
||||||
|
stream, streamErr := spdyConn.CreateStream(http.Header{}, nil, false)
|
||||||
|
if streamErr != nil {
|
||||||
|
t.Fatalf("Error creating stream: %s", streamErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := []byte("dskjahfkdusahfkdsahfkdsafdkas")
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
if _, err := stream.Write(buf); err != nil {
|
||||||
|
t.Fatalf("Error writing to stream: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
if _, err := stream.Read(buf); err != nil {
|
||||||
|
t.Fatalf("Error reading from stream: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fmt.Printf("Resetting...\n")
|
||||||
|
if err := stream.Reset(); err != nil {
|
||||||
|
t.Fatalf("Error reseting stream: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
closeErr := server.Close()
|
||||||
|
if closeErr != nil {
|
||||||
|
t.Fatalf("Error shutting down server: %s", closeErr)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStreamResetWithDataRemaining(t *testing.T) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
server, listen, serverErr := runServer(&wg)
|
||||||
|
if serverErr != nil {
|
||||||
|
t.Fatalf("Error initializing server: %s", serverErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, dialErr := net.Dial("tcp", listen)
|
||||||
|
if dialErr != nil {
|
||||||
|
t.Fatalf("Error dialing server: %s", dialErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
spdyConn, spdyErr := NewConnection(conn, false)
|
||||||
|
if spdyErr != nil {
|
||||||
|
t.Fatalf("Error creating spdy connection: %s", spdyErr)
|
||||||
|
}
|
||||||
|
go spdyConn.Serve(NoOpStreamHandler)
|
||||||
|
|
||||||
|
authenticated = true
|
||||||
|
stream, streamErr := spdyConn.CreateStream(http.Header{}, nil, false)
|
||||||
|
if streamErr != nil {
|
||||||
|
t.Fatalf("Error creating stream: %s", streamErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := []byte("dskjahfkdusahfkdsahfkdsafdkas")
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
if _, err := stream.Write(buf); err != nil {
|
||||||
|
t.Fatalf("Error writing to stream: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// read a bit to make sure a goroutine gets to <-dataChan
|
||||||
|
if _, err := stream.Read(buf); err != nil {
|
||||||
|
t.Fatalf("Error reading from stream: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fmt.Printf("Resetting...\n")
|
||||||
|
if err := stream.Reset(); err != nil {
|
||||||
|
t.Fatalf("Error reseting stream: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
closeErr := server.Close()
|
||||||
|
if closeErr != nil {
|
||||||
|
t.Fatalf("Error shutting down server: %s", closeErr)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
var authenticated bool
|
||||||
|
|
||||||
|
func authStreamHandler(stream *Stream) {
|
||||||
|
if !authenticated {
|
||||||
|
stream.Refuse()
|
||||||
|
}
|
||||||
|
MirrorStreamHandler(stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runServer(wg *sync.WaitGroup) (io.Closer, string, error) {
|
||||||
|
listener, listenErr := net.Listen("tcp", "localhost:0")
|
||||||
|
if listenErr != nil {
|
||||||
|
return nil, "", listenErr
|
||||||
|
}
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
conn, connErr := listener.Accept()
|
||||||
|
if connErr != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
spdyConn, _ := NewConnection(conn, true)
|
||||||
|
go spdyConn.Serve(authStreamHandler)
|
||||||
|
|
||||||
|
}
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
return listener, listener.Addr().String(), nil
|
||||||
|
}
|
328
Godeps/_workspace/src/github.com/docker/spdystream/stream.go
generated
vendored
Normal file
328
Godeps/_workspace/src/github.com/docker/spdystream/stream.go
generated
vendored
Normal file
@@ -0,0 +1,328 @@
|
|||||||
|
package spdystream
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.google.com/p/go.net/spdy"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrUnreadPartialData = errors.New("unread partial data")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Stream struct {
|
||||||
|
streamId spdy.StreamId
|
||||||
|
parent *Stream
|
||||||
|
conn *Connection
|
||||||
|
startChan chan error
|
||||||
|
|
||||||
|
dataLock sync.RWMutex
|
||||||
|
dataChan chan []byte
|
||||||
|
unread []byte
|
||||||
|
|
||||||
|
priority uint8
|
||||||
|
headers http.Header
|
||||||
|
headerChan chan http.Header
|
||||||
|
finishLock sync.Mutex
|
||||||
|
finished bool
|
||||||
|
replyCond *sync.Cond
|
||||||
|
replied bool
|
||||||
|
closeLock sync.Mutex
|
||||||
|
closeChan chan bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteData writes data to stream, sending a dataframe per call
|
||||||
|
func (s *Stream) WriteData(data []byte, fin bool) error {
|
||||||
|
s.waitWriteReply()
|
||||||
|
var flags spdy.DataFlags
|
||||||
|
|
||||||
|
if fin {
|
||||||
|
flags = spdy.DataFlagFin
|
||||||
|
s.finishLock.Lock()
|
||||||
|
if s.finished {
|
||||||
|
s.finishLock.Unlock()
|
||||||
|
return ErrWriteClosedStream
|
||||||
|
}
|
||||||
|
s.finished = true
|
||||||
|
s.finishLock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
dataFrame := &spdy.DataFrame{
|
||||||
|
StreamId: s.streamId,
|
||||||
|
Flags: flags,
|
||||||
|
Data: data,
|
||||||
|
}
|
||||||
|
|
||||||
|
s.conn.writeLock.Lock()
|
||||||
|
defer s.conn.writeLock.Unlock()
|
||||||
|
debugMessage("(%p) (%d) Writing data frame", s, s.streamId)
|
||||||
|
return s.conn.framer.WriteFrame(dataFrame)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes bytes to a stream, calling write data for each call.
|
||||||
|
func (s *Stream) Write(data []byte) (n int, err error) {
|
||||||
|
err = s.WriteData(data, false)
|
||||||
|
if err == nil {
|
||||||
|
n = len(data)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads bytes from a stream, a single read will never get more
|
||||||
|
// than what is sent on a single data frame, but a multiple calls to
|
||||||
|
// read may get data from the same data frame.
|
||||||
|
func (s *Stream) Read(p []byte) (n int, err error) {
|
||||||
|
if s.unread == nil {
|
||||||
|
select {
|
||||||
|
case <-s.closeChan:
|
||||||
|
return 0, io.EOF
|
||||||
|
case read, ok := <-s.dataChan:
|
||||||
|
if !ok {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
s.unread = read
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n = copy(p, s.unread)
|
||||||
|
if n < len(s.unread) {
|
||||||
|
s.unread = s.unread[n:]
|
||||||
|
} else {
|
||||||
|
s.unread = nil
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadData reads an entire data frame and returns the byte array
|
||||||
|
// from the data frame. If there is unread data from the result
|
||||||
|
// of a Read call, this function will return an ErrUnreadPartialData.
|
||||||
|
func (s *Stream) ReadData() ([]byte, error) {
|
||||||
|
debugMessage("(%p) Reading data from %d", s, s.streamId)
|
||||||
|
if s.unread != nil {
|
||||||
|
return nil, ErrUnreadPartialData
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-s.closeChan:
|
||||||
|
return nil, io.EOF
|
||||||
|
case read, ok := <-s.dataChan:
|
||||||
|
if !ok {
|
||||||
|
return nil, io.EOF
|
||||||
|
}
|
||||||
|
return read, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) waitWriteReply() {
|
||||||
|
if s.replyCond != nil {
|
||||||
|
s.replyCond.L.Lock()
|
||||||
|
for !s.replied {
|
||||||
|
s.replyCond.Wait()
|
||||||
|
}
|
||||||
|
s.replyCond.L.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait waits for the stream to receive a reply.
|
||||||
|
func (s *Stream) Wait() error {
|
||||||
|
return s.WaitTimeout(time.Duration(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitTimeout waits for the stream to receive a reply or for timeout.
|
||||||
|
// When the timeout is reached, ErrTimeout will be returned.
|
||||||
|
func (s *Stream) WaitTimeout(timeout time.Duration) error {
|
||||||
|
var timeoutChan <-chan time.Time
|
||||||
|
if timeout > time.Duration(0) {
|
||||||
|
timeoutChan = time.After(timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err := <-s.startChan:
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case <-timeoutChan:
|
||||||
|
return ErrTimeout
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the stream by sending an empty data frame with the
|
||||||
|
// finish flag set, indicating this side is finished with the stream.
|
||||||
|
func (s *Stream) Close() error {
|
||||||
|
select {
|
||||||
|
case <-s.closeChan:
|
||||||
|
// Stream is now fully closed
|
||||||
|
s.conn.removeStream(s)
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return s.WriteData([]byte{}, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset sends a reset frame, putting the stream into the fully closed state.
|
||||||
|
func (s *Stream) Reset() error {
|
||||||
|
s.conn.removeStream(s)
|
||||||
|
|
||||||
|
s.finishLock.Lock()
|
||||||
|
if s.finished {
|
||||||
|
s.finishLock.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
s.finished = true
|
||||||
|
s.finishLock.Unlock()
|
||||||
|
|
||||||
|
s.closeRemoteChannels()
|
||||||
|
|
||||||
|
resetFrame := &spdy.RstStreamFrame{
|
||||||
|
StreamId: s.streamId,
|
||||||
|
Status: spdy.Cancel,
|
||||||
|
}
|
||||||
|
s.conn.writeLock.Lock()
|
||||||
|
defer s.conn.writeLock.Unlock()
|
||||||
|
return s.conn.framer.WriteFrame(resetFrame)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateSubStream creates a stream using the current as the parent
|
||||||
|
func (s *Stream) CreateSubStream(headers http.Header, fin bool) (*Stream, error) {
|
||||||
|
return s.conn.CreateStream(headers, s, fin)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPriority sets the stream priority, does not affect the
|
||||||
|
// remote priority of this stream after Open has been called.
|
||||||
|
// Valid values are 0 through 7, 0 being the highest priority
|
||||||
|
// and 7 the lowest.
|
||||||
|
func (s *Stream) SetPriority(priority uint8) {
|
||||||
|
s.priority = priority
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendHeader sends a header frame across the stream
|
||||||
|
func (s *Stream) SendHeader(headers http.Header, fin bool) error {
|
||||||
|
return s.conn.sendHeaders(headers, s, fin)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendReply sends a reply on a stream, only valid to be called once
|
||||||
|
// when handling a new stream
|
||||||
|
func (s *Stream) SendReply(headers http.Header, fin bool) error {
|
||||||
|
if s.replyCond == nil {
|
||||||
|
return errors.New("cannot reply on initiated stream")
|
||||||
|
}
|
||||||
|
s.replyCond.L.Lock()
|
||||||
|
defer s.replyCond.L.Unlock()
|
||||||
|
if s.replied {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := s.conn.sendReply(headers, s, fin)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.replied = true
|
||||||
|
s.replyCond.Broadcast()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refuse sends a reset frame with the status refuse, only
|
||||||
|
// valid to be called once when handling a new stream. This
|
||||||
|
// may be used to indicate that a stream is not allowed
|
||||||
|
// when http status codes are not being used.
|
||||||
|
func (s *Stream) Refuse() error {
|
||||||
|
if s.replied {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
s.replied = true
|
||||||
|
return s.conn.sendReset(spdy.RefusedStream, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancel sends a reset frame with the status canceled. This
|
||||||
|
// can be used at any time by the creator of the Stream to
|
||||||
|
// indicate the stream is no longer needed.
|
||||||
|
func (s *Stream) Cancel() error {
|
||||||
|
return s.conn.sendReset(spdy.Cancel, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReceiveHeader receives a header sent on the other side
|
||||||
|
// of the stream. This function will block until a header
|
||||||
|
// is received or stream is closed.
|
||||||
|
func (s *Stream) ReceiveHeader() (http.Header, error) {
|
||||||
|
select {
|
||||||
|
case <-s.closeChan:
|
||||||
|
break
|
||||||
|
case header, ok := <-s.headerChan:
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("header chan closed")
|
||||||
|
}
|
||||||
|
return header, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("stream closed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parent returns the parent stream
|
||||||
|
func (s *Stream) Parent() *Stream {
|
||||||
|
return s.parent
|
||||||
|
}
|
||||||
|
|
||||||
|
// Headers returns the headers used to create the stream
|
||||||
|
func (s *Stream) Headers() http.Header {
|
||||||
|
return s.headers
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string version of stream using the
|
||||||
|
// streamId to uniquely identify the stream
|
||||||
|
func (s *Stream) String() string {
|
||||||
|
return fmt.Sprintf("stream:%d", s.streamId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Identifier returns a 32 bit identifier for the stream
|
||||||
|
func (s *Stream) Identifier() uint32 {
|
||||||
|
return uint32(s.streamId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsFinished returns whether the stream has finished
|
||||||
|
// sending data
|
||||||
|
func (s *Stream) IsFinished() bool {
|
||||||
|
return s.finished
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement net.Conn interface
|
||||||
|
|
||||||
|
func (s *Stream) LocalAddr() net.Addr {
|
||||||
|
return s.conn.conn.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) RemoteAddr() net.Addr {
|
||||||
|
return s.conn.conn.RemoteAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO set per stream values instead of connection-wide
|
||||||
|
|
||||||
|
func (s *Stream) SetDeadline(t time.Time) error {
|
||||||
|
return s.conn.conn.SetDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) SetReadDeadline(t time.Time) error {
|
||||||
|
return s.conn.conn.SetReadDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) SetWriteDeadline(t time.Time) error {
|
||||||
|
return s.conn.conn.SetWriteDeadline(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) closeRemoteChannels() {
|
||||||
|
s.closeLock.Lock()
|
||||||
|
defer s.closeLock.Unlock()
|
||||||
|
select {
|
||||||
|
case <-s.closeChan:
|
||||||
|
default:
|
||||||
|
close(s.closeChan)
|
||||||
|
s.dataLock.Lock()
|
||||||
|
defer s.dataLock.Unlock()
|
||||||
|
close(s.dataChan)
|
||||||
|
}
|
||||||
|
}
|
16
Godeps/_workspace/src/github.com/docker/spdystream/utils.go
generated
vendored
Normal file
16
Godeps/_workspace/src/github.com/docker/spdystream/utils.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package spdystream
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DEBUG = os.Getenv("DEBUG")
|
||||||
|
)
|
||||||
|
|
||||||
|
func debugMessage(fmt string, args ...interface{}) {
|
||||||
|
if DEBUG != "" {
|
||||||
|
log.Printf(fmt, args...)
|
||||||
|
}
|
||||||
|
}
|
65
Godeps/_workspace/src/github.com/docker/spdystream/ws/connection.go
generated
vendored
Normal file
65
Godeps/_workspace/src/github.com/docker/spdystream/ws/connection.go
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
package ws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Wrap an HTTP2 connection over WebSockets and
|
||||||
|
// use the underlying WebSocket framing for proxy
|
||||||
|
// compatibility.
|
||||||
|
type Conn struct {
|
||||||
|
*websocket.Conn
|
||||||
|
reader io.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnection(w *websocket.Conn) *Conn {
|
||||||
|
return &Conn{Conn: w}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Conn) Write(b []byte) (int, error) {
|
||||||
|
err := c.WriteMessage(websocket.BinaryMessage, b)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return len(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Conn) Read(b []byte) (int, error) {
|
||||||
|
if c.reader == nil {
|
||||||
|
t, r, err := c.NextReader()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if t != websocket.BinaryMessage {
|
||||||
|
log.Printf("ws: ignored non-binary message in stream")
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
c.reader = r
|
||||||
|
}
|
||||||
|
n, err := c.reader.Read(b)
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
c.reader = nil
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Conn) SetDeadline(t time.Time) error {
|
||||||
|
if err := c.Conn.SetReadDeadline(t); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := c.Conn.SetWriteDeadline(t); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Conn) Close() error {
|
||||||
|
err := c.Conn.Close()
|
||||||
|
return err
|
||||||
|
}
|
175
Godeps/_workspace/src/github.com/docker/spdystream/ws/ws_test.go
generated
vendored
Normal file
175
Godeps/_workspace/src/github.com/docker/spdystream/ws/ws_test.go
generated
vendored
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
package ws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"github.com/docker/spdystream"
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var upgrader = websocket.Upgrader{
|
||||||
|
ReadBufferSize: 1024,
|
||||||
|
WriteBufferSize: 1024,
|
||||||
|
}
|
||||||
|
|
||||||
|
var serverSpdyConn *spdystream.Connection
|
||||||
|
|
||||||
|
// Connect to the Websocket endpoint at ws://localhost
|
||||||
|
// using SPDY over Websockets framing.
|
||||||
|
func ExampleConn() {
|
||||||
|
wsconn, _, _ := websocket.DefaultDialer.Dial("ws://localhost/", http.Header{"Origin": {"http://localhost/"}})
|
||||||
|
conn, _ := spdystream.NewConnection(NewConnection(wsconn), false)
|
||||||
|
go conn.Serve(spdystream.NoOpStreamHandler, spdystream.NoAuthHandler)
|
||||||
|
stream, _ := conn.CreateStream(http.Header{}, nil, false)
|
||||||
|
stream.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func serveWs(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != "GET" {
|
||||||
|
http.Error(w, "Method not allowed", 405)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ws, err := upgrader.Upgrade(w, r, nil)
|
||||||
|
if err != nil {
|
||||||
|
if _, ok := err.(websocket.HandshakeError); !ok {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
wrap := NewConnection(ws)
|
||||||
|
spdyConn, err := spdystream.NewConnection(wrap, true)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
serverSpdyConn = spdyConn
|
||||||
|
go spdyConn.Serve(spdystream.MirrorStreamHandler, authStreamHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSpdyStreamOverWs(t *testing.T) {
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(serveWs))
|
||||||
|
defer server.Close()
|
||||||
|
defer func() {
|
||||||
|
if serverSpdyConn != nil {
|
||||||
|
serverSpdyConn.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
wsconn, _, err := websocket.DefaultDialer.Dial(strings.Replace(server.URL, "http://", "ws://", 1), http.Header{"Origin": {server.URL}})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wrap := NewConnection(wsconn)
|
||||||
|
spdyConn, err := spdystream.NewConnection(wrap, false)
|
||||||
|
if err != nil {
|
||||||
|
defer wsconn.Close()
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer spdyConn.Close()
|
||||||
|
authenticated = true
|
||||||
|
go spdyConn.Serve(spdystream.NoOpStreamHandler, spdystream.RejectAuthHandler)
|
||||||
|
|
||||||
|
stream, streamErr := spdyConn.CreateStream(http.Header{}, nil, false)
|
||||||
|
if streamErr != nil {
|
||||||
|
t.Fatalf("Error creating stream: %s", streamErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
waitErr := stream.Wait()
|
||||||
|
if waitErr != nil {
|
||||||
|
t.Fatalf("Error waiting for stream: %s", waitErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
message := []byte("hello")
|
||||||
|
writeErr := stream.WriteData(message, false)
|
||||||
|
if writeErr != nil {
|
||||||
|
t.Fatalf("Error writing data")
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, 10)
|
||||||
|
n, readErr := stream.Read(buf)
|
||||||
|
if readErr != nil {
|
||||||
|
t.Fatalf("Error reading data from stream: %s", readErr)
|
||||||
|
}
|
||||||
|
if n != 5 {
|
||||||
|
t.Fatalf("Unexpected number of bytes read:\nActual: %d\nExpected: 5", n)
|
||||||
|
}
|
||||||
|
if bytes.Compare(buf[:n], message) != 0 {
|
||||||
|
t.Fatalf("Did not receive expected message:\nActual: %s\nExpectd: %s", buf, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
writeErr = stream.WriteData(message, true)
|
||||||
|
if writeErr != nil {
|
||||||
|
t.Fatalf("Error writing data")
|
||||||
|
}
|
||||||
|
|
||||||
|
smallBuf := make([]byte, 3)
|
||||||
|
n, readErr = stream.Read(smallBuf)
|
||||||
|
if readErr != nil {
|
||||||
|
t.Fatalf("Error reading data from stream: %s", readErr)
|
||||||
|
}
|
||||||
|
if n != 3 {
|
||||||
|
t.Fatalf("Unexpected number of bytes read:\nActual: %d\nExpected: 3", n)
|
||||||
|
}
|
||||||
|
if bytes.Compare(smallBuf[:n], []byte("hel")) != 0 {
|
||||||
|
t.Fatalf("Did not receive expected message:\nActual: %s\nExpectd: %s", smallBuf[:n], message)
|
||||||
|
}
|
||||||
|
n, readErr = stream.Read(smallBuf)
|
||||||
|
if readErr != nil {
|
||||||
|
t.Fatalf("Error reading data from stream: %s", readErr)
|
||||||
|
}
|
||||||
|
if n != 2 {
|
||||||
|
t.Fatalf("Unexpected number of bytes read:\nActual: %d\nExpected: 2", n)
|
||||||
|
}
|
||||||
|
if bytes.Compare(smallBuf[:n], []byte("lo")) != 0 {
|
||||||
|
t.Fatalf("Did not receive expected message:\nActual: %s\nExpected: lo", smallBuf[:n])
|
||||||
|
}
|
||||||
|
|
||||||
|
n, readErr = stream.Read(buf)
|
||||||
|
if readErr != io.EOF {
|
||||||
|
t.Fatalf("Expected EOF reading from finished stream, read %d bytes", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
streamCloseErr := stream.Close()
|
||||||
|
if streamCloseErr != nil {
|
||||||
|
t.Fatalf("Error closing stream: %s", streamCloseErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Closing again should return nil
|
||||||
|
streamCloseErr = stream.Close()
|
||||||
|
if streamCloseErr != nil {
|
||||||
|
t.Fatalf("Error closing stream: %s", streamCloseErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticated = false
|
||||||
|
badStream, badStreamErr := spdyConn.CreateStream(http.Header{}, nil, false)
|
||||||
|
if badStreamErr != nil {
|
||||||
|
t.Fatalf("Error creating stream: %s", badStreamErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
waitErr = badStream.Wait()
|
||||||
|
if waitErr == nil {
|
||||||
|
t.Fatalf("Did not receive error creating stream")
|
||||||
|
}
|
||||||
|
if waitErr != spdystream.ErrReset {
|
||||||
|
t.Fatalf("Unexpected error creating stream: %s", waitErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
spdyCloseErr := spdyConn.Close()
|
||||||
|
if spdyCloseErr != nil {
|
||||||
|
t.Fatalf("Error closing spdy connection: %s", spdyCloseErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var authenticated bool
|
||||||
|
|
||||||
|
func authStreamHandler(header http.Header, slot uint8, parent uint32) bool {
|
||||||
|
return authenticated
|
||||||
|
}
|
4
Godeps/_workspace/src/github.com/kr/pty/.gitignore
generated
vendored
Normal file
4
Godeps/_workspace/src/github.com/kr/pty/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[568].out
|
||||||
|
_go*
|
||||||
|
_test*
|
||||||
|
_obj
|
23
Godeps/_workspace/src/github.com/kr/pty/License
generated
vendored
Normal file
23
Godeps/_workspace/src/github.com/kr/pty/License
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
Copyright (c) 2011 Keith Rarick
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated
|
||||||
|
documentation files (the "Software"), to deal in the
|
||||||
|
Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute,
|
||||||
|
sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall
|
||||||
|
be included in all copies or substantial portions of the
|
||||||
|
Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
|
||||||
|
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||||
|
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
||||||
|
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
36
Godeps/_workspace/src/github.com/kr/pty/README.md
generated
vendored
Normal file
36
Godeps/_workspace/src/github.com/kr/pty/README.md
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# pty
|
||||||
|
|
||||||
|
Pty is a Go package for using unix pseudo-terminals.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
go get github.com/kr/pty
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kr/pty"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
c := exec.Command("grep", "--color=auto", "bar")
|
||||||
|
f, err := pty.Start(c)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
f.Write([]byte("foo\n"))
|
||||||
|
f.Write([]byte("bar\n"))
|
||||||
|
f.Write([]byte("baz\n"))
|
||||||
|
f.Write([]byte{4}) // EOT
|
||||||
|
}()
|
||||||
|
io.Copy(os.Stdout, f)
|
||||||
|
}
|
||||||
|
```
|
16
Godeps/_workspace/src/github.com/kr/pty/doc.go
generated
vendored
Normal file
16
Godeps/_workspace/src/github.com/kr/pty/doc.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
// Package pty provides functions for working with Unix terminals.
|
||||||
|
package pty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrUnsupported is returned if a function is not
|
||||||
|
// available on the current platform.
|
||||||
|
var ErrUnsupported = errors.New("unsupported")
|
||||||
|
|
||||||
|
// Opens a pty and its corresponding tty.
|
||||||
|
func Open() (pty, tty *os.File, err error) {
|
||||||
|
return open()
|
||||||
|
}
|
11
Godeps/_workspace/src/github.com/kr/pty/ioctl.go
generated
vendored
Normal file
11
Godeps/_workspace/src/github.com/kr/pty/ioctl.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package pty
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
func ioctl(fd, cmd, ptr uintptr) error {
|
||||||
|
_, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, cmd, ptr)
|
||||||
|
if e != 0 {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
39
Godeps/_workspace/src/github.com/kr/pty/ioctl_bsd.go
generated
vendored
Normal file
39
Godeps/_workspace/src/github.com/kr/pty/ioctl_bsd.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// +build darwin dragonfly freebsd netbsd openbsd
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
// from <sys/ioccom.h>
|
||||||
|
const (
|
||||||
|
_IOC_VOID uintptr = 0x20000000
|
||||||
|
_IOC_OUT uintptr = 0x40000000
|
||||||
|
_IOC_IN uintptr = 0x80000000
|
||||||
|
_IOC_IN_OUT uintptr = _IOC_OUT | _IOC_IN
|
||||||
|
_IOC_DIRMASK = _IOC_VOID | _IOC_OUT | _IOC_IN
|
||||||
|
|
||||||
|
_IOC_PARAM_SHIFT = 13
|
||||||
|
_IOC_PARAM_MASK = (1 << _IOC_PARAM_SHIFT) - 1
|
||||||
|
)
|
||||||
|
|
||||||
|
func _IOC_PARM_LEN(ioctl uintptr) uintptr {
|
||||||
|
return (ioctl >> 16) & _IOC_PARAM_MASK
|
||||||
|
}
|
||||||
|
|
||||||
|
func _IOC(inout uintptr, group byte, ioctl_num uintptr, param_len uintptr) uintptr {
|
||||||
|
return inout | (param_len&_IOC_PARAM_MASK)<<16 | uintptr(group)<<8 | ioctl_num
|
||||||
|
}
|
||||||
|
|
||||||
|
func _IO(group byte, ioctl_num uintptr) uintptr {
|
||||||
|
return _IOC(_IOC_VOID, group, ioctl_num, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _IOR(group byte, ioctl_num uintptr, param_len uintptr) uintptr {
|
||||||
|
return _IOC(_IOC_OUT, group, ioctl_num, param_len)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _IOW(group byte, ioctl_num uintptr, param_len uintptr) uintptr {
|
||||||
|
return _IOC(_IOC_IN, group, ioctl_num, param_len)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _IOWR(group byte, ioctl_num uintptr, param_len uintptr) uintptr {
|
||||||
|
return _IOC(_IOC_IN_OUT, group, ioctl_num, param_len)
|
||||||
|
}
|
19
Godeps/_workspace/src/github.com/kr/pty/mktypes.bash
generated
vendored
Normal file
19
Godeps/_workspace/src/github.com/kr/pty/mktypes.bash
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
GOOSARCH="${GOOS}_${GOARCH}"
|
||||||
|
case "$GOOSARCH" in
|
||||||
|
_* | *_ | _)
|
||||||
|
echo 'undefined $GOOS_$GOARCH:' "$GOOSARCH" 1>&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
GODEFS="go tool cgo -godefs"
|
||||||
|
|
||||||
|
$GODEFS types.go |gofmt > ztypes_$GOARCH.go
|
||||||
|
|
||||||
|
case $GOOS in
|
||||||
|
freebsd)
|
||||||
|
$GODEFS types_$GOOS.go |gofmt > ztypes_$GOOSARCH.go
|
||||||
|
;;
|
||||||
|
esac
|
60
Godeps/_workspace/src/github.com/kr/pty/pty_darwin.go
generated
vendored
Normal file
60
Godeps/_workspace/src/github.com/kr/pty/pty_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package pty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func open() (pty, tty *os.File, err error) {
|
||||||
|
p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sname, err := ptsname(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = grantpt(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = unlockpt(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := os.OpenFile(sname, os.O_RDWR, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return p, t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ptsname(f *os.File) (string, error) {
|
||||||
|
n := make([]byte, _IOC_PARM_LEN(syscall.TIOCPTYGNAME))
|
||||||
|
|
||||||
|
err := ioctl(f.Fd(), syscall.TIOCPTYGNAME, uintptr(unsafe.Pointer(&n[0])))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, c := range n {
|
||||||
|
if c == 0 {
|
||||||
|
return string(n[:i]), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", errors.New("TIOCPTYGNAME string not NUL-terminated")
|
||||||
|
}
|
||||||
|
|
||||||
|
func grantpt(f *os.File) error {
|
||||||
|
return ioctl(f.Fd(), syscall.TIOCPTYGRANT, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unlockpt(f *os.File) error {
|
||||||
|
return ioctl(f.Fd(), syscall.TIOCPTYUNLK, 0)
|
||||||
|
}
|
73
Godeps/_workspace/src/github.com/kr/pty/pty_freebsd.go
generated
vendored
Normal file
73
Godeps/_workspace/src/github.com/kr/pty/pty_freebsd.go
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
package pty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func posix_openpt(oflag int) (fd int, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall(syscall.SYS_POSIX_OPENPT, uintptr(oflag), 0, 0)
|
||||||
|
fd = int(r0)
|
||||||
|
if e1 != 0 {
|
||||||
|
err = e1
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func open() (pty, tty *os.File, err error) {
|
||||||
|
fd, err := posix_openpt(syscall.O_RDWR | syscall.O_CLOEXEC)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
p := os.NewFile(uintptr(fd), "/dev/pts")
|
||||||
|
sname, err := ptsname(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := os.OpenFile("/dev/"+sname, os.O_RDWR, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return p, t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isptmaster(fd uintptr) (bool, error) {
|
||||||
|
err := ioctl(fd, syscall.TIOCPTMASTER, 0)
|
||||||
|
return err == nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
emptyFiodgnameArg fiodgnameArg
|
||||||
|
ioctl_FIODGNAME = _IOW('f', 120, unsafe.Sizeof(emptyFiodgnameArg))
|
||||||
|
)
|
||||||
|
|
||||||
|
func ptsname(f *os.File) (string, error) {
|
||||||
|
master, err := isptmaster(f.Fd())
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if !master {
|
||||||
|
return "", syscall.EINVAL
|
||||||
|
}
|
||||||
|
|
||||||
|
const n = _C_SPECNAMELEN + 1
|
||||||
|
var (
|
||||||
|
buf = make([]byte, n)
|
||||||
|
arg = fiodgnameArg{Len: n, Buf: (*byte)(unsafe.Pointer(&buf[0]))}
|
||||||
|
)
|
||||||
|
err = ioctl(f.Fd(), ioctl_FIODGNAME, uintptr(unsafe.Pointer(&arg)))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, c := range buf {
|
||||||
|
if c == 0 {
|
||||||
|
return string(buf[:i]), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", errors.New("FIODGNAME string not NUL-terminated")
|
||||||
|
}
|
46
Godeps/_workspace/src/github.com/kr/pty/pty_linux.go
generated
vendored
Normal file
46
Godeps/_workspace/src/github.com/kr/pty/pty_linux.go
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package pty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func open() (pty, tty *os.File, err error) {
|
||||||
|
p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sname, err := ptsname(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = unlockpt(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := os.OpenFile(sname, os.O_RDWR|syscall.O_NOCTTY, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return p, t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ptsname(f *os.File) (string, error) {
|
||||||
|
var n _C_uint
|
||||||
|
err := ioctl(f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n)))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return "/dev/pts/" + strconv.Itoa(int(n)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unlockpt(f *os.File) error {
|
||||||
|
var u _C_int
|
||||||
|
// use TIOCSPTLCK with a zero valued arg to clear the slave pty lock
|
||||||
|
return ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u)))
|
||||||
|
}
|
11
Godeps/_workspace/src/github.com/kr/pty/pty_unsupported.go
generated
vendored
Normal file
11
Godeps/_workspace/src/github.com/kr/pty/pty_unsupported.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
// +build !linux,!darwin,!freebsd
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func open() (pty, tty *os.File, err error) {
|
||||||
|
return nil, nil, ErrUnsupported
|
||||||
|
}
|
28
Godeps/_workspace/src/github.com/kr/pty/run.go
generated
vendored
Normal file
28
Godeps/_workspace/src/github.com/kr/pty/run.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package pty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Start assigns a pseudo-terminal tty os.File to c.Stdin, c.Stdout,
|
||||||
|
// and c.Stderr, calls c.Start, and returns the File of the tty's
|
||||||
|
// corresponding pty.
|
||||||
|
func Start(c *exec.Cmd) (pty *os.File, err error) {
|
||||||
|
pty, tty, err := Open()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer tty.Close()
|
||||||
|
c.Stdout = tty
|
||||||
|
c.Stdin = tty
|
||||||
|
c.Stderr = tty
|
||||||
|
c.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true}
|
||||||
|
err = c.Start()
|
||||||
|
if err != nil {
|
||||||
|
pty.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return pty, err
|
||||||
|
}
|
10
Godeps/_workspace/src/github.com/kr/pty/types.go
generated
vendored
Normal file
10
Godeps/_workspace/src/github.com/kr/pty/types.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
// +build ignore
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
type (
|
||||||
|
_C_int C.int
|
||||||
|
_C_uint C.uint
|
||||||
|
)
|
15
Godeps/_workspace/src/github.com/kr/pty/types_freebsd.go
generated
vendored
Normal file
15
Godeps/_workspace/src/github.com/kr/pty/types_freebsd.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// +build ignore
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/filio.h>
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
const (
|
||||||
|
_C_SPECNAMELEN = C.SPECNAMELEN /* max length of devicename */
|
||||||
|
)
|
||||||
|
|
||||||
|
type fiodgnameArg C.struct_fiodgname_arg
|
35
Godeps/_workspace/src/github.com/kr/pty/util.go
generated
vendored
Normal file
35
Godeps/_workspace/src/github.com/kr/pty/util.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package pty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Getsize returns the number of rows (lines) and cols (positions
|
||||||
|
// in each line) in terminal t.
|
||||||
|
func Getsize(t *os.File) (rows, cols int, err error) {
|
||||||
|
var ws winsize
|
||||||
|
err = windowrect(&ws, t.Fd())
|
||||||
|
return int(ws.ws_row), int(ws.ws_col), err
|
||||||
|
}
|
||||||
|
|
||||||
|
type winsize struct {
|
||||||
|
ws_row uint16
|
||||||
|
ws_col uint16
|
||||||
|
ws_xpixel uint16
|
||||||
|
ws_ypixel uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowrect(ws *winsize, fd uintptr) error {
|
||||||
|
_, _, errno := syscall.Syscall(
|
||||||
|
syscall.SYS_IOCTL,
|
||||||
|
fd,
|
||||||
|
syscall.TIOCGWINSZ,
|
||||||
|
uintptr(unsafe.Pointer(ws)),
|
||||||
|
)
|
||||||
|
if errno != 0 {
|
||||||
|
return syscall.Errno(errno)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
9
Godeps/_workspace/src/github.com/kr/pty/ztypes_386.go
generated
vendored
Normal file
9
Godeps/_workspace/src/github.com/kr/pty/ztypes_386.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
// Created by cgo -godefs - DO NOT EDIT
|
||||||
|
// cgo -godefs types.go
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
type (
|
||||||
|
_C_int int32
|
||||||
|
_C_uint uint32
|
||||||
|
)
|
9
Godeps/_workspace/src/github.com/kr/pty/ztypes_amd64.go
generated
vendored
Normal file
9
Godeps/_workspace/src/github.com/kr/pty/ztypes_amd64.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
// Created by cgo -godefs - DO NOT EDIT
|
||||||
|
// cgo -godefs types.go
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
type (
|
||||||
|
_C_int int32
|
||||||
|
_C_uint uint32
|
||||||
|
)
|
9
Godeps/_workspace/src/github.com/kr/pty/ztypes_arm.go
generated
vendored
Normal file
9
Godeps/_workspace/src/github.com/kr/pty/ztypes_arm.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
// Created by cgo -godefs - DO NOT EDIT
|
||||||
|
// cgo -godefs types.go
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
type (
|
||||||
|
_C_int int32
|
||||||
|
_C_uint uint32
|
||||||
|
)
|
13
Godeps/_workspace/src/github.com/kr/pty/ztypes_freebsd_386.go
generated
vendored
Normal file
13
Godeps/_workspace/src/github.com/kr/pty/ztypes_freebsd_386.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// Created by cgo -godefs - DO NOT EDIT
|
||||||
|
// cgo -godefs types_freebsd.go
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
const (
|
||||||
|
_C_SPECNAMELEN = 0x3f
|
||||||
|
)
|
||||||
|
|
||||||
|
type fiodgnameArg struct {
|
||||||
|
Len int32
|
||||||
|
Buf *byte
|
||||||
|
}
|
14
Godeps/_workspace/src/github.com/kr/pty/ztypes_freebsd_amd64.go
generated
vendored
Normal file
14
Godeps/_workspace/src/github.com/kr/pty/ztypes_freebsd_amd64.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
// Created by cgo -godefs - DO NOT EDIT
|
||||||
|
// cgo -godefs types_freebsd.go
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
const (
|
||||||
|
_C_SPECNAMELEN = 0x3f
|
||||||
|
)
|
||||||
|
|
||||||
|
type fiodgnameArg struct {
|
||||||
|
Len int32
|
||||||
|
Pad_cgo_0 [4]byte
|
||||||
|
Buf *byte
|
||||||
|
}
|
13
Godeps/_workspace/src/github.com/kr/pty/ztypes_freebsd_arm.go
generated
vendored
Normal file
13
Godeps/_workspace/src/github.com/kr/pty/ztypes_freebsd_arm.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// Created by cgo -godefs - DO NOT EDIT
|
||||||
|
// cgo -godefs types_freebsd.go
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
const (
|
||||||
|
_C_SPECNAMELEN = 0x3f
|
||||||
|
)
|
||||||
|
|
||||||
|
type fiodgnameArg struct {
|
||||||
|
Len int32
|
||||||
|
Buf *byte
|
||||||
|
}
|
11
Godeps/_workspace/src/github.com/kr/pty/ztypes_ppc64.go
generated
vendored
Normal file
11
Godeps/_workspace/src/github.com/kr/pty/ztypes_ppc64.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
// +build ppc64
|
||||||
|
|
||||||
|
// Created by cgo -godefs - DO NOT EDIT
|
||||||
|
// cgo -godefs types.go
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
type (
|
||||||
|
_C_int int32
|
||||||
|
_C_uint uint32
|
||||||
|
)
|
11
Godeps/_workspace/src/github.com/kr/pty/ztypes_ppc64le.go
generated
vendored
Normal file
11
Godeps/_workspace/src/github.com/kr/pty/ztypes_ppc64le.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
// +build ppc64le
|
||||||
|
|
||||||
|
// Created by cgo -godefs - DO NOT EDIT
|
||||||
|
// cgo -godefs types.go
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
type (
|
||||||
|
_C_int int32
|
||||||
|
_C_uint uint32
|
||||||
|
)
|
11
Godeps/_workspace/src/github.com/kr/pty/ztypes_s390x.go
generated
vendored
Normal file
11
Godeps/_workspace/src/github.com/kr/pty/ztypes_s390x.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
// +build s390x
|
||||||
|
|
||||||
|
// Created by cgo -godefs - DO NOT EDIT
|
||||||
|
// cgo -godefs types.go
|
||||||
|
|
||||||
|
package pty
|
||||||
|
|
||||||
|
type (
|
||||||
|
_C_int int32
|
||||||
|
_C_uint uint32
|
||||||
|
)
|
Reference in New Issue
Block a user