Commit 3ed435ba authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #40801 from deads2k/client-23-sync

Automatic merge from submit-queue (batch tested with PRs 40801, 40797) sync client-go Our first straight mechanical sync! `client-go/pkg` looks nice and slim, we're authoritative for all other packages. @caesarxuchao @sttts
parents 96cfe7b9 f3f7c984
...@@ -75,14 +75,6 @@ ...@@ -75,14 +75,6 @@
"Rev": "cd27f179f2c10c5d300e6d09025b538c475b0d51" "Rev": "cd27f179f2c10c5d300e6d09025b538c475b0d51"
}, },
{ {
"ImportPath": "github.com/docker/spdystream",
"Rev": "449fdfce4d962303d702fec724ef0ad181c92528"
},
{
"ImportPath": "github.com/docker/spdystream/spdy",
"Rev": "449fdfce4d962303d702fec724ef0ad181c92528"
},
{
"ImportPath": "github.com/emicklei/go-restful", "ImportPath": "github.com/emicklei/go-restful",
"Comment": "v1.2-96-g09691a3", "Comment": "v1.2-96-g09691a3",
"Rev": "09691a3b6378b740595c1002f40c34dd5f218a22" "Rev": "09691a3b6378b740595c1002f40c34dd5f218a22"
...@@ -324,183 +316,175 @@ ...@@ -324,183 +316,175 @@
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/api/equality", "ImportPath": "k8s.io/apimachinery/pkg/api/equality",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/api/errors", "ImportPath": "k8s.io/apimachinery/pkg/api/errors",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/api/meta", "ImportPath": "k8s.io/apimachinery/pkg/api/meta",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/api/resource", "ImportPath": "k8s.io/apimachinery/pkg/api/resource",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/apimachinery", "ImportPath": "k8s.io/apimachinery/pkg/apimachinery",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/apimachinery/announced", "ImportPath": "k8s.io/apimachinery/pkg/apimachinery/announced",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/apimachinery/registered", "ImportPath": "k8s.io/apimachinery/pkg/apimachinery/registered",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1", "ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured", "ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/conversion", "ImportPath": "k8s.io/apimachinery/pkg/conversion",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/conversion/queryparams", "ImportPath": "k8s.io/apimachinery/pkg/conversion/queryparams",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/fields", "ImportPath": "k8s.io/apimachinery/pkg/fields",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/labels", "ImportPath": "k8s.io/apimachinery/pkg/labels",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/openapi", "ImportPath": "k8s.io/apimachinery/pkg/openapi",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime", "ImportPath": "k8s.io/apimachinery/pkg/runtime",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime/schema", "ImportPath": "k8s.io/apimachinery/pkg/runtime/schema",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer", "ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/json", "ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/json",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/protobuf", "ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/protobuf",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/recognizer", "ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/recognizer",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/streaming", "ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/streaming",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/versioning", "ImportPath": "k8s.io/apimachinery/pkg/runtime/serializer/versioning",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/selection", "ImportPath": "k8s.io/apimachinery/pkg/selection",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/types", "ImportPath": "k8s.io/apimachinery/pkg/types",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/diff", "ImportPath": "k8s.io/apimachinery/pkg/util/diff",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/errors", "ImportPath": "k8s.io/apimachinery/pkg/util/errors",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/framer", "ImportPath": "k8s.io/apimachinery/pkg/util/framer",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/httpstream", "ImportPath": "k8s.io/apimachinery/pkg/util/httpstream",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
},
{
"ImportPath": "k8s.io/apimachinery/pkg/util/httpstream/spdy",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/intstr", "ImportPath": "k8s.io/apimachinery/pkg/util/intstr",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/json", "ImportPath": "k8s.io/apimachinery/pkg/util/json",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/net", "ImportPath": "k8s.io/apimachinery/pkg/util/net",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/rand", "ImportPath": "k8s.io/apimachinery/pkg/util/rand",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/runtime", "ImportPath": "k8s.io/apimachinery/pkg/util/runtime",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/sets", "ImportPath": "k8s.io/apimachinery/pkg/util/sets",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/strategicpatch", "ImportPath": "k8s.io/apimachinery/pkg/util/strategicpatch",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/validation", "ImportPath": "k8s.io/apimachinery/pkg/util/validation",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/validation/field", "ImportPath": "k8s.io/apimachinery/pkg/util/validation/field",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/wait", "ImportPath": "k8s.io/apimachinery/pkg/util/wait",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/yaml", "ImportPath": "k8s.io/apimachinery/pkg/util/yaml",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/version", "ImportPath": "k8s.io/apimachinery/pkg/version",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/pkg/watch", "ImportPath": "k8s.io/apimachinery/pkg/watch",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/third_party/forked/golang/json", "ImportPath": "k8s.io/apimachinery/third_party/forked/golang/json",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
},
{
"ImportPath": "k8s.io/apimachinery/third_party/forked/golang/netutil",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44"
}, },
{ {
"ImportPath": "k8s.io/apimachinery/third_party/forked/golang/reflect", "ImportPath": "k8s.io/apimachinery/third_party/forked/golang/reflect",
"Rev": "21807b270ec15d19215659a5caa08b17f66d6f44" "Rev": "7080e31e90e981181435294bca96c80a37db8941"
} }
] ]
} }
# 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!
# Spdystream maintainers file
#
# This file describes who runs the docker/spdystream project and how.
# This is a living document - if you see something out of date or missing, speak up!
#
# It is structured to be consumable by both humans and programs.
# To extract its contents programmatically, use any TOML-compliant parser.
#
# This file is compiled into the MAINTAINERS file in docker/opensource.
#
[Org]
[Org."Core maintainers"]
people = [
"dmcgowan",
]
[people]
# A reference list of all people associated with the project.
# All other sections should refer to people by their canonical key
# in the people section.
# ADD YOURSELF HERE IN ALPHABETICAL ORDER
[people.dmcgowan]
Name = "Derek McGowan"
Email = "derek@docker.com"
GitHub = "dmcgowan"
# 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
Copyright © 2014-2015 Docker, Inc. All rights reserved, except as follows. Code is released under the Apache 2.0 license. The README.md file, and files in the "docs" folder are licensed under the Creative Commons Attribution 4.0 International License under the terms and conditions set forth in the file "LICENSE.docs". You may obtain a duplicate copy of the same license, titled CC-BY-SA-4.0, at http://creativecommons.org/licenses/by/4.0/.
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)
}
package spdystream
import (
"container/heap"
"sync"
"github.com/docker/spdystream/spdy"
)
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()
}
// 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,
}
// 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
}
// 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
}
package spdystream
import (
"errors"
"fmt"
"io"
"net"
"net/http"
"sync"
"time"
"github.com/docker/spdystream/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,
}
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)
return s.resetStream()
}
func (s *Stream) resetStream() error {
// Always call closeRemoteChannels, even if s.finished is already true.
// This makes it so that stream.Close() followed by stream.Reset() allows
// stream.Read() to unblock.
s.closeRemoteChannels()
s.finishLock.Lock()
if s.finished {
s.finishLock.Unlock()
return nil
}
s.finished = true
s.finishLock.Unlock()
resetFrame := &spdy.RstStreamFrame{
StreamId: s.streamId,
Status: spdy.Cancel,
}
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)
}
}
package spdystream
import (
"log"
"os"
)
var (
DEBUG = os.Getenv("DEBUG")
)
func debugMessage(fmt string, args ...interface{}) {
if DEBUG != "" {
log.Printf(fmt, args...)
}
}
...@@ -406,3 +406,8 @@ func parseSelector(selector string, fn TransformFunc) (Selector, error) { ...@@ -406,3 +406,8 @@ func parseSelector(selector string, fn TransformFunc) (Selector, error) {
func OneTermEqualSelector(k, v string) Selector { func OneTermEqualSelector(k, v string) Selector {
return &hasTerm{field: k, value: v} return &hasTerm{field: k, value: v}
} }
// AndSelectors creates a selector that is the logical AND of all the given selectors
func AndSelectors(selectors ...Selector) Selector {
return andTerm(selectors)
}
/*
Copyright 2015 The Kubernetes Authors.
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.
*/
package spdy
import (
"net"
"net/http"
"sync"
"time"
"github.com/docker/spdystream"
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/util/httpstream"
)
// connection maintains state about a spdystream.Connection and its associated
// streams.
type connection struct {
conn *spdystream.Connection
streams []httpstream.Stream
streamLock sync.Mutex
newStreamHandler httpstream.NewStreamHandler
}
// NewClientConnection creates a new SPDY client connection.
func NewClientConnection(conn net.Conn) (httpstream.Connection, error) {
spdyConn, err := spdystream.NewConnection(conn, false)
if err != nil {
defer conn.Close()
return nil, err
}
return newConnection(spdyConn, httpstream.NoOpNewStreamHandler), nil
}
// NewServerConnection creates a new SPDY server connection. newStreamHandler
// will be invoked when the server receives a newly created stream from the
// client.
func NewServerConnection(conn net.Conn, newStreamHandler httpstream.NewStreamHandler) (httpstream.Connection, error) {
spdyConn, err := spdystream.NewConnection(conn, true)
if err != nil {
defer conn.Close()
return nil, err
}
return newConnection(spdyConn, newStreamHandler), nil
}
// newConnection returns a new connection wrapping conn. newStreamHandler
// will be invoked when the server receives a newly created stream from the
// client.
func newConnection(conn *spdystream.Connection, newStreamHandler httpstream.NewStreamHandler) httpstream.Connection {
c := &connection{conn: conn, newStreamHandler: newStreamHandler}
go conn.Serve(c.newSpdyStream)
return c
}
// createStreamResponseTimeout indicates how long to wait for the other side to
// acknowledge the new stream before timing out.
const createStreamResponseTimeout = 30 * time.Second
// Close first sends a reset for all of the connection's streams, and then
// closes the underlying spdystream.Connection.
func (c *connection) Close() error {
c.streamLock.Lock()
for _, s := range c.streams {
// calling Reset instead of Close ensures that all streams are fully torn down
s.Reset()
}
c.streams = make([]httpstream.Stream, 0)
c.streamLock.Unlock()
// now that all streams are fully torn down, it's safe to call close on the underlying connection,
// which should be able to terminate immediately at this point, instead of waiting for any
// remaining graceful stream termination.
return c.conn.Close()
}
// CreateStream creates a new stream with the specified headers and registers
// it with the connection.
func (c *connection) CreateStream(headers http.Header) (httpstream.Stream, error) {
stream, err := c.conn.CreateStream(headers, nil, false)
if err != nil {
return nil, err
}
if err = stream.WaitTimeout(createStreamResponseTimeout); err != nil {
return nil, err
}
c.registerStream(stream)
return stream, nil
}
// registerStream adds the stream s to the connection's list of streams that
// it owns.
func (c *connection) registerStream(s httpstream.Stream) {
c.streamLock.Lock()
c.streams = append(c.streams, s)
c.streamLock.Unlock()
}
// CloseChan returns a channel that, when closed, indicates that the underlying
// spdystream.Connection has been closed.
func (c *connection) CloseChan() <-chan bool {
return c.conn.CloseChan()
}
// newSpdyStream is the internal new stream handler used by spdystream.Connection.Serve.
// It calls connection's newStreamHandler, giving it the opportunity to accept or reject
// the stream. If newStreamHandler returns an error, the stream is rejected. If not, the
// stream is accepted and registered with the connection.
func (c *connection) newSpdyStream(stream *spdystream.Stream) {
replySent := make(chan struct{})
err := c.newStreamHandler(stream, replySent)
rejectStream := (err != nil)
if rejectStream {
glog.Warningf("Stream rejected: %v", err)
stream.Reset()
return
}
c.registerStream(stream)
stream.SendReply(http.Header{}, rejectStream)
close(replySent)
}
// SetIdleTimeout sets the amount of time the connection may remain idle before
// it is automatically closed.
func (c *connection) SetIdleTimeout(timeout time.Duration) {
c.conn.SetIdleTimeout(timeout)
}
/*
Copyright 2015 The Kubernetes Authors.
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.
*/
package spdy
import (
"bufio"
"crypto/tls"
"encoding/base64"
"fmt"
"io/ioutil"
"net"
"net/http"
"net/http/httputil"
"net/url"
"strings"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/util/httpstream"
"k8s.io/apimachinery/third_party/forked/golang/netutil"
)
// SpdyRoundTripper knows how to upgrade an HTTP request to one that supports
// multiplexed streams. After RoundTrip() is invoked, Conn will be set
// and usable. SpdyRoundTripper implements the UpgradeRoundTripper interface.
type SpdyRoundTripper struct {
//tlsConfig holds the TLS configuration settings to use when connecting
//to the remote server.
tlsConfig *tls.Config
/* TODO according to http://golang.org/pkg/net/http/#RoundTripper, a RoundTripper
must be safe for use by multiple concurrent goroutines. If this is absolutely
necessary, we could keep a map from http.Request to net.Conn. In practice,
a client will create an http.Client, set the transport to a new insteace of
SpdyRoundTripper, and use it a single time, so this hopefully won't be an issue.
*/
// conn is the underlying network connection to the remote server.
conn net.Conn
// Dialer is the dialer used to connect. Used if non-nil.
Dialer *net.Dialer
// proxier knows which proxy to use given a request, defaults to http.ProxyFromEnvironment
// Used primarily for mocking the proxy discovery in tests.
proxier func(req *http.Request) (*url.URL, error)
}
// NewRoundTripper creates a new SpdyRoundTripper that will use
// the specified tlsConfig.
func NewRoundTripper(tlsConfig *tls.Config) httpstream.UpgradeRoundTripper {
return NewSpdyRoundTripper(tlsConfig)
}
// NewSpdyRoundTripper creates a new SpdyRoundTripper that will use
// the specified tlsConfig. This function is mostly meant for unit tests.
func NewSpdyRoundTripper(tlsConfig *tls.Config) *SpdyRoundTripper {
return &SpdyRoundTripper{tlsConfig: tlsConfig}
}
// implements pkg/util/net.TLSClientConfigHolder for proper TLS checking during proxying with a spdy roundtripper
func (s *SpdyRoundTripper) TLSClientConfig() *tls.Config {
return s.tlsConfig
}
// dial dials the host specified by req, using TLS if appropriate, optionally
// using a proxy server if one is configured via environment variables.
func (s *SpdyRoundTripper) dial(req *http.Request) (net.Conn, error) {
proxier := s.proxier
if proxier == nil {
proxier = http.ProxyFromEnvironment
}
proxyURL, err := proxier(req)
if err != nil {
return nil, err
}
if proxyURL == nil {
return s.dialWithoutProxy(req.URL)
}
// ensure we use a canonical host with proxyReq
targetHost := netutil.CanonicalAddr(req.URL)
// proxying logic adapted from http://blog.h6t.eu/post/74098062923/golang-websocket-with-http-proxy-support
proxyReq := http.Request{
Method: "CONNECT",
URL: &url.URL{},
Host: targetHost,
}
if pa := s.proxyAuth(proxyURL); pa != "" {
proxyReq.Header = http.Header{}
proxyReq.Header.Set("Proxy-Authorization", pa)
}
proxyDialConn, err := s.dialWithoutProxy(proxyURL)
if err != nil {
return nil, err
}
proxyClientConn := httputil.NewProxyClientConn(proxyDialConn, nil)
_, err = proxyClientConn.Do(&proxyReq)
if err != nil && err != httputil.ErrPersistEOF {
return nil, err
}
rwc, _ := proxyClientConn.Hijack()
if req.URL.Scheme != "https" {
return rwc, nil
}
host, _, err := net.SplitHostPort(targetHost)
if err != nil {
return nil, err
}
if s.tlsConfig == nil {
s.tlsConfig = &tls.Config{}
}
if len(s.tlsConfig.ServerName) == 0 {
s.tlsConfig.ServerName = host
}
tlsConn := tls.Client(rwc, s.tlsConfig)
// need to manually call Handshake() so we can call VerifyHostname() below
if err := tlsConn.Handshake(); err != nil {
return nil, err
}
// Return if we were configured to skip validation
if s.tlsConfig != nil && s.tlsConfig.InsecureSkipVerify {
return tlsConn, nil
}
if err := tlsConn.VerifyHostname(host); err != nil {
return nil, err
}
return tlsConn, nil
}
// dialWithoutProxy dials the host specified by url, using TLS if appropriate.
func (s *SpdyRoundTripper) dialWithoutProxy(url *url.URL) (net.Conn, error) {
dialAddr := netutil.CanonicalAddr(url)
if url.Scheme == "http" {
if s.Dialer == nil {
return net.Dial("tcp", dialAddr)
} else {
return s.Dialer.Dial("tcp", dialAddr)
}
}
// TODO validate the TLSClientConfig is set up?
var conn *tls.Conn
var err error
if s.Dialer == nil {
conn, err = tls.Dial("tcp", dialAddr, s.tlsConfig)
} else {
conn, err = tls.DialWithDialer(s.Dialer, "tcp", dialAddr, s.tlsConfig)
}
if err != nil {
return nil, err
}
// Return if we were configured to skip validation
if s.tlsConfig != nil && s.tlsConfig.InsecureSkipVerify {
return conn, nil
}
host, _, err := net.SplitHostPort(dialAddr)
if err != nil {
return nil, err
}
err = conn.VerifyHostname(host)
if err != nil {
return nil, err
}
return conn, nil
}
// proxyAuth returns, for a given proxy URL, the value to be used for the Proxy-Authorization header
func (s *SpdyRoundTripper) proxyAuth(proxyURL *url.URL) string {
if proxyURL == nil || proxyURL.User == nil {
return ""
}
credentials := proxyURL.User.String()
encodedAuth := base64.StdEncoding.EncodeToString([]byte(credentials))
return fmt.Sprintf("Basic %s", encodedAuth)
}
// RoundTrip executes the Request and upgrades it. After a successful upgrade,
// clients may call SpdyRoundTripper.Connection() to retrieve the upgraded
// connection.
func (s *SpdyRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
// TODO what's the best way to clone the request?
r := *req
req = &r
req.Header.Add(httpstream.HeaderConnection, httpstream.HeaderUpgrade)
req.Header.Add(httpstream.HeaderUpgrade, HeaderSpdy31)
conn, err := s.dial(req)
if err != nil {
return nil, err
}
err = req.Write(conn)
if err != nil {
return nil, err
}
resp, err := http.ReadResponse(bufio.NewReader(conn), req)
if err != nil {
return nil, err
}
s.conn = conn
return resp, nil
}
// NewConnection validates the upgrade response, creating and returning a new
// httpstream.Connection if there were no errors.
func (s *SpdyRoundTripper) NewConnection(resp *http.Response) (httpstream.Connection, error) {
connectionHeader := strings.ToLower(resp.Header.Get(httpstream.HeaderConnection))
upgradeHeader := strings.ToLower(resp.Header.Get(httpstream.HeaderUpgrade))
if (resp.StatusCode != http.StatusSwitchingProtocols) || !strings.Contains(connectionHeader, strings.ToLower(httpstream.HeaderUpgrade)) || !strings.Contains(upgradeHeader, strings.ToLower(HeaderSpdy31)) {
defer resp.Body.Close()
responseError := ""
responseErrorBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
responseError = "unable to read error from server response"
} else {
// TODO: I don't belong here, I should be abstracted from this class
if obj, _, err := statusCodecs.UniversalDecoder().Decode(responseErrorBytes, nil, &metav1.Status{}); err == nil {
if status, ok := obj.(*metav1.Status); ok {
return nil, &apierrors.StatusError{ErrStatus: *status}
}
}
responseError = string(responseErrorBytes)
responseError = strings.TrimSpace(responseError)
}
return nil, fmt.Errorf("unable to upgrade connection: %s", responseError)
}
return NewClientConnection(s.conn)
}
// statusScheme is private scheme for the decoding here until someone fixes the TODO in NewConnection
var statusScheme = runtime.NewScheme()
// ParameterCodec knows about query parameters used with the meta v1 API spec.
var statusCodecs = serializer.NewCodecFactory(statusScheme)
func init() {
statusScheme.AddUnversionedTypes(metav1.SchemeGroupVersion,
&metav1.Status{},
)
}
/*
Copyright 2015 The Kubernetes Authors.
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.
*/
package spdy
import (
"fmt"
"net/http"
"strings"
"k8s.io/apimachinery/pkg/util/httpstream"
"k8s.io/apimachinery/pkg/util/runtime"
)
const HeaderSpdy31 = "SPDY/3.1"
// responseUpgrader knows how to upgrade HTTP responses. It
// implements the httpstream.ResponseUpgrader interface.
type responseUpgrader struct {
}
// NewResponseUpgrader returns a new httpstream.ResponseUpgrader that is
// capable of upgrading HTTP responses using SPDY/3.1 via the
// spdystream package.
func NewResponseUpgrader() httpstream.ResponseUpgrader {
return responseUpgrader{}
}
// UpgradeResponse upgrades an HTTP response to one that supports multiplexed
// streams. newStreamHandler will be called synchronously whenever the
// other end of the upgraded connection creates a new stream.
func (u responseUpgrader) UpgradeResponse(w http.ResponseWriter, req *http.Request, newStreamHandler httpstream.NewStreamHandler) httpstream.Connection {
connectionHeader := strings.ToLower(req.Header.Get(httpstream.HeaderConnection))
upgradeHeader := strings.ToLower(req.Header.Get(httpstream.HeaderUpgrade))
if !strings.Contains(connectionHeader, strings.ToLower(httpstream.HeaderUpgrade)) || !strings.Contains(upgradeHeader, strings.ToLower(HeaderSpdy31)) {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "unable to upgrade: missing upgrade headers in request: %#v", req.Header)
return nil
}
hijacker, ok := w.(http.Hijacker)
if !ok {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "unable to upgrade: unable to hijack response")
return nil
}
w.Header().Add(httpstream.HeaderConnection, httpstream.HeaderUpgrade)
w.Header().Add(httpstream.HeaderUpgrade, HeaderSpdy31)
w.WriteHeader(http.StatusSwitchingProtocols)
conn, _, err := hijacker.Hijack()
if err != nil {
runtime.HandleError(fmt.Errorf("unable to upgrade: error hijacking response: %v", err))
return nil
}
spdyConn, err := NewServerConnection(conn, newStreamHandler)
if err != nil {
runtime.HandleError(fmt.Errorf("unable to upgrade: error creating SPDY server connection: %v", err))
return nil
}
return spdyConn
}
...@@ -548,7 +548,7 @@ func StrategicMergeMapPatch(original, patch JSONMap, dataStruct interface{}) (JS ...@@ -548,7 +548,7 @@ func StrategicMergeMapPatch(original, patch JSONMap, dataStruct interface{}) (JS
if err != nil { if err != nil {
return nil, err return nil, err
} }
return mergeMap(original, patch, t, true) return mergeMap(original, patch, t, true, true)
} }
func getTagStructType(dataStruct interface{}) (reflect.Type, error) { func getTagStructType(dataStruct interface{}) (reflect.Type, error) {
...@@ -574,7 +574,10 @@ var errBadPatchTypeFmt = "unknown patch type: %s in map: %v" ...@@ -574,7 +574,10 @@ var errBadPatchTypeFmt = "unknown patch type: %s in map: %v"
// both the original map and the patch because getting a deep copy of a map in // both the original map and the patch because getting a deep copy of a map in
// golang is highly non-trivial. // golang is highly non-trivial.
// flag mergeDeleteList controls if using the parallel list to delete or keeping the list. // flag mergeDeleteList controls if using the parallel list to delete or keeping the list.
func mergeMap(original, patch map[string]interface{}, t reflect.Type, mergeDeleteList bool) (map[string]interface{}, error) { // If patch contains any null field (e.g. field_1: null) that is not
// present in original, then to propagate it to the end result use
// ignoreUnmatchedNulls == false.
func mergeMap(original, patch map[string]interface{}, t reflect.Type, mergeDeleteList, ignoreUnmatchedNulls bool) (map[string]interface{}, error) {
if v, ok := patch[directiveMarker]; ok { if v, ok := patch[directiveMarker]; ok {
if v == replaceDirective { if v == replaceDirective {
// If the patch contains "$patch: replace", don't merge it, just use the // If the patch contains "$patch: replace", don't merge it, just use the
...@@ -619,13 +622,17 @@ func mergeMap(original, patch map[string]interface{}, t reflect.Type, mergeDelet ...@@ -619,13 +622,17 @@ func mergeMap(original, patch map[string]interface{}, t reflect.Type, mergeDelet
} }
// If the value of this key is null, delete the key if it exists in the // If the value of this key is null, delete the key if it exists in the
// original. Otherwise, skip it. // original. Otherwise, check if we want to preserve it or skip it.
// Preserving the null value is useful when we want to send an explicit
// delete to the API server.
if patchV == nil { if patchV == nil {
if _, ok := original[k]; ok { if _, ok := original[k]; ok {
delete(original, k) delete(original, k)
} }
continue if ignoreUnmatchedNulls {
continue
}
} }
_, ok := original[k] _, ok := original[k]
...@@ -654,7 +661,7 @@ func mergeMap(original, patch map[string]interface{}, t reflect.Type, mergeDelet ...@@ -654,7 +661,7 @@ func mergeMap(original, patch map[string]interface{}, t reflect.Type, mergeDelet
typedOriginal := original[k].(map[string]interface{}) typedOriginal := original[k].(map[string]interface{})
typedPatch := patchV.(map[string]interface{}) typedPatch := patchV.(map[string]interface{})
var err error var err error
original[k], err = mergeMap(typedOriginal, typedPatch, fieldType, mergeDeleteList) original[k], err = mergeMap(typedOriginal, typedPatch, fieldType, mergeDeleteList, ignoreUnmatchedNulls)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -667,7 +674,7 @@ func mergeMap(original, patch map[string]interface{}, t reflect.Type, mergeDelet ...@@ -667,7 +674,7 @@ func mergeMap(original, patch map[string]interface{}, t reflect.Type, mergeDelet
typedOriginal := original[k].([]interface{}) typedOriginal := original[k].([]interface{})
typedPatch := patchV.([]interface{}) typedPatch := patchV.([]interface{})
var err error var err error
original[k], err = mergeSlice(typedOriginal, typedPatch, elemType, fieldPatchMergeKey, mergeDeleteList, isDeleteList) original[k], err = mergeSlice(typedOriginal, typedPatch, elemType, fieldPatchMergeKey, mergeDeleteList, isDeleteList, ignoreUnmatchedNulls)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -688,7 +695,7 @@ func mergeMap(original, patch map[string]interface{}, t reflect.Type, mergeDelet ...@@ -688,7 +695,7 @@ func mergeMap(original, patch map[string]interface{}, t reflect.Type, mergeDelet
// Merge two slices together. Note: This may modify both the original slice and // Merge two slices together. Note: This may modify both the original slice and
// the patch because getting a deep copy of a slice in golang is highly // the patch because getting a deep copy of a slice in golang is highly
// non-trivial. // non-trivial.
func mergeSlice(original, patch []interface{}, elemType reflect.Type, mergeKey string, mergeDeleteList, isDeleteList bool) ([]interface{}, error) { func mergeSlice(original, patch []interface{}, elemType reflect.Type, mergeKey string, mergeDeleteList, isDeleteList, ignoreUnmatchedNulls bool) ([]interface{}, error) {
if len(original) == 0 && len(patch) == 0 { if len(original) == 0 && len(patch) == 0 {
return original, nil return original, nil
} }
...@@ -779,7 +786,7 @@ func mergeSlice(original, patch []interface{}, elemType reflect.Type, mergeKey s ...@@ -779,7 +786,7 @@ func mergeSlice(original, patch []interface{}, elemType reflect.Type, mergeKey s
var mergedMaps interface{} var mergedMaps interface{}
var err error var err error
// Merge into original. // Merge into original.
mergedMaps, err = mergeMap(originalMap, typedV, elemType, mergeDeleteList) mergedMaps, err = mergeMap(originalMap, typedV, elemType, mergeDeleteList, ignoreUnmatchedNulls)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -1282,7 +1289,8 @@ func mapsOfMapsHaveConflicts(typedLeft, typedRight map[string]interface{}, struc ...@@ -1282,7 +1289,8 @@ func mapsOfMapsHaveConflicts(typedLeft, typedRight map[string]interface{}, struc
// configurations. Conflicts are defined as keys changed differently from original to modified // configurations. Conflicts are defined as keys changed differently from original to modified
// than from original to current. In other words, a conflict occurs if modified changes any key // than from original to current. In other words, a conflict occurs if modified changes any key
// in a way that is different from how it is changed in current (e.g., deleting it, changing its // in a way that is different from how it is changed in current (e.g., deleting it, changing its
// value). // value). We also propagate values fields that do not exist in original but are explicitly
// defined in modified.
func CreateThreeWayMergePatch(original, modified, current []byte, dataStruct interface{}, overwrite bool, fns ...PreconditionFunc) ([]byte, error) { func CreateThreeWayMergePatch(original, modified, current []byte, dataStruct interface{}, overwrite bool, fns ...PreconditionFunc) ([]byte, error) {
originalMap := map[string]interface{}{} originalMap := map[string]interface{}{}
if len(original) > 0 { if len(original) > 0 {
...@@ -1324,7 +1332,7 @@ func CreateThreeWayMergePatch(original, modified, current []byte, dataStruct int ...@@ -1324,7 +1332,7 @@ func CreateThreeWayMergePatch(original, modified, current []byte, dataStruct int
return nil, err return nil, err
} }
patchMap, err := mergeMap(deletionsMap, deltaMap, t, false) patchMap, err := mergeMap(deletionsMap, deltaMap, t, false, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
package netutil
import (
"net/url"
"strings"
)
// FROM: http://golang.org/src/net/http/client.go
// Given a string of the form "host", "host:port", or "[ipv6::address]:port",
// return true if the string includes a port.
func hasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }
// FROM: http://golang.org/src/net/http/transport.go
var portMap = map[string]string{
"http": "80",
"https": "443",
}
// FROM: http://golang.org/src/net/http/transport.go
// canonicalAddr returns url.Host but always with a ":port" suffix
func CanonicalAddr(url *url.URL) string {
addr := url.Host
if !hasPort(addr) {
return addr + ":" + portMap[url.Scheme]
}
return addr
}
/*
Copyright 2015 The Kubernetes Authors.
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.
*/
// package portforward contains server-side logic for handling port forwarding requests.
package portforward
// The subprotocol "portforward.k8s.io" is used for port forwarding.
const PortForwardProtocolV1Name = "portforward.k8s.io"
...@@ -9340,30 +9340,6 @@ go_library( ...@@ -9340,30 +9340,6 @@ go_library(
) )
go_library( go_library(
name = "k8s.io/client-go/_vendor/github.com/docker/spdystream",
srcs = [
"k8s.io/client-go/_vendor/github.com/docker/spdystream/connection.go",
"k8s.io/client-go/_vendor/github.com/docker/spdystream/handlers.go",
"k8s.io/client-go/_vendor/github.com/docker/spdystream/priority.go",
"k8s.io/client-go/_vendor/github.com/docker/spdystream/stream.go",
"k8s.io/client-go/_vendor/github.com/docker/spdystream/utils.go",
],
tags = ["automanaged"],
deps = ["//vendor:github.com/docker/spdystream/spdy"],
)
go_library(
name = "k8s.io/client-go/_vendor/github.com/docker/spdystream/spdy",
srcs = [
"k8s.io/client-go/_vendor/github.com/docker/spdystream/spdy/dictionary.go",
"k8s.io/client-go/_vendor/github.com/docker/spdystream/spdy/read.go",
"k8s.io/client-go/_vendor/github.com/docker/spdystream/spdy/types.go",
"k8s.io/client-go/_vendor/github.com/docker/spdystream/spdy/write.go",
],
tags = ["automanaged"],
)
go_library(
name = "k8s.io/client-go/_vendor/github.com/emicklei/go-restful", name = "k8s.io/client-go/_vendor/github.com/emicklei/go-restful",
srcs = [ srcs = [
"k8s.io/client-go/_vendor/github.com/emicklei/go-restful/compress.go", "k8s.io/client-go/_vendor/github.com/emicklei/go-restful/compress.go",
...@@ -12070,23 +12046,6 @@ go_library( ...@@ -12070,23 +12046,6 @@ go_library(
) )
go_library( go_library(
name = "k8s.io/client-go/pkg/kubelet/server/portforward",
srcs = [
"k8s.io/client-go/pkg/kubelet/server/portforward/constants.go",
"k8s.io/client-go/pkg/kubelet/server/portforward/portforward.go",
],
tags = ["automanaged"],
deps = [
"//vendor:github.com/golang/glog",
"//vendor:k8s.io/apimachinery/pkg/types",
"//vendor:k8s.io/apimachinery/pkg/util/httpstream",
"//vendor:k8s.io/apimachinery/pkg/util/httpstream/spdy",
"//vendor:k8s.io/apimachinery/pkg/util/runtime",
"//vendor:k8s.io/client-go/pkg/api",
],
)
go_library(
name = "k8s.io/client-go/pkg/util", name = "k8s.io/client-go/pkg/util",
srcs = [ srcs = [
"k8s.io/client-go/pkg/util/doc.go", "k8s.io/client-go/pkg/util/doc.go",
...@@ -13838,12 +13797,6 @@ go_library( ...@@ -13838,12 +13797,6 @@ go_library(
) )
go_library( go_library(
name = "k8s.io/client-go/_vendor/k8s.io/apimachinery/third_party/forked/golang/netutil",
srcs = ["k8s.io/client-go/_vendor/k8s.io/apimachinery/third_party/forked/golang/netutil/addr.go"],
tags = ["automanaged"],
)
go_library(
name = "k8s.io/client-go/third_party/forked/golang/template", name = "k8s.io/client-go/third_party/forked/golang/template",
srcs = [ srcs = [
"k8s.io/client-go/third_party/forked/golang/template/exec.go", "k8s.io/client-go/third_party/forked/golang/template/exec.go",
...@@ -13964,27 +13917,6 @@ go_library( ...@@ -13964,27 +13917,6 @@ go_library(
) )
go_library( go_library(
name = "k8s.io/client-go/_vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy",
srcs = [
"k8s.io/client-go/_vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/connection.go",
"k8s.io/client-go/_vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/roundtripper.go",
"k8s.io/client-go/_vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/upgrade.go",
],
tags = ["automanaged"],
deps = [
"//vendor:github.com/docker/spdystream",
"//vendor:github.com/golang/glog",
"//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/runtime/serializer",
"//vendor:k8s.io/apimachinery/pkg/util/httpstream",
"//vendor:k8s.io/apimachinery/pkg/util/runtime",
"//vendor:k8s.io/apimachinery/third_party/forked/golang/netutil",
],
)
go_library(
name = "k8s.io/client-go/_vendor/k8s.io/apimachinery/pkg/util/intstr", name = "k8s.io/client-go/_vendor/k8s.io/apimachinery/pkg/util/intstr",
srcs = [ srcs = [
"k8s.io/client-go/_vendor/k8s.io/apimachinery/pkg/util/intstr/generated.pb.go", "k8s.io/client-go/_vendor/k8s.io/apimachinery/pkg/util/intstr/generated.pb.go",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment