mirror of https://github.com/k3s-io/k3s
Update golang.org/x/net for http2 enablement
parent
b61ce0bc81
commit
786b7989a0
|
@ -1927,43 +1927,43 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/context",
|
"ImportPath": "golang.org/x/net/context",
|
||||||
"Rev": "c2528b2dd8352441850638a8bb678c2ad056fd3e"
|
"Rev": "62685c2d7ca23c807425dca88b11a3e2323dab41"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/context/ctxhttp",
|
"ImportPath": "golang.org/x/net/context/ctxhttp",
|
||||||
"Rev": "c2528b2dd8352441850638a8bb678c2ad056fd3e"
|
"Rev": "62685c2d7ca23c807425dca88b11a3e2323dab41"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/html",
|
"ImportPath": "golang.org/x/net/html",
|
||||||
"Rev": "c2528b2dd8352441850638a8bb678c2ad056fd3e"
|
"Rev": "62685c2d7ca23c807425dca88b11a3e2323dab41"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/html/atom",
|
"ImportPath": "golang.org/x/net/html/atom",
|
||||||
"Rev": "c2528b2dd8352441850638a8bb678c2ad056fd3e"
|
"Rev": "62685c2d7ca23c807425dca88b11a3e2323dab41"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2",
|
"ImportPath": "golang.org/x/net/http2",
|
||||||
"Rev": "c2528b2dd8352441850638a8bb678c2ad056fd3e"
|
"Rev": "62685c2d7ca23c807425dca88b11a3e2323dab41"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/http2/hpack",
|
"ImportPath": "golang.org/x/net/http2/hpack",
|
||||||
"Rev": "c2528b2dd8352441850638a8bb678c2ad056fd3e"
|
"Rev": "62685c2d7ca23c807425dca88b11a3e2323dab41"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/internal/timeseries",
|
"ImportPath": "golang.org/x/net/internal/timeseries",
|
||||||
"Rev": "c2528b2dd8352441850638a8bb678c2ad056fd3e"
|
"Rev": "62685c2d7ca23c807425dca88b11a3e2323dab41"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/proxy",
|
"ImportPath": "golang.org/x/net/proxy",
|
||||||
"Rev": "c2528b2dd8352441850638a8bb678c2ad056fd3e"
|
"Rev": "62685c2d7ca23c807425dca88b11a3e2323dab41"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/trace",
|
"ImportPath": "golang.org/x/net/trace",
|
||||||
"Rev": "c2528b2dd8352441850638a8bb678c2ad056fd3e"
|
"Rev": "62685c2d7ca23c807425dca88b11a3e2323dab41"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/websocket",
|
"ImportPath": "golang.org/x/net/websocket",
|
||||||
"Rev": "c2528b2dd8352441850638a8bb678c2ad056fd3e"
|
"Rev": "62685c2d7ca23c807425dca88b11a3e2323dab41"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/oauth2",
|
"ImportPath": "golang.org/x/oauth2",
|
||||||
|
|
|
@ -189,7 +189,7 @@ func Background() Context {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO returns a non-nil, empty Context. Code should use context.TODO when
|
// TODO returns a non-nil, empty Context. Code should use context.TODO when
|
||||||
// it's unclear which Context to use or it's is not yet available (because the
|
// it's unclear which Context to use or it is not yet available (because the
|
||||||
// surrounding function has not yet been extended to accept a Context
|
// surrounding function has not yet been extended to accept a Context
|
||||||
// parameter). TODO is recognized by static analysis tools that determine
|
// parameter). TODO is recognized by static analysis tools that determine
|
||||||
// whether Contexts are propagated correctly in a program.
|
// whether Contexts are propagated correctly in a program.
|
||||||
|
|
|
@ -9,6 +9,7 @@ package ctxhttp
|
||||||
import "net/http"
|
import "net/http"
|
||||||
|
|
||||||
func canceler(client *http.Client, req *http.Request) func() {
|
func canceler(client *http.Client, req *http.Request) func() {
|
||||||
|
// TODO(djd): Respect any existing value of req.Cancel.
|
||||||
ch := make(chan struct{})
|
ch := make(chan struct{})
|
||||||
req.Cancel = ch
|
req.Cancel = ch
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,14 @@ import (
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func nop() {}
|
||||||
|
|
||||||
|
var (
|
||||||
|
testHookContextDoneBeforeHeaders = nop
|
||||||
|
testHookDoReturned = nop
|
||||||
|
testHookDidBodyClose = nop
|
||||||
|
)
|
||||||
|
|
||||||
// Do sends an HTTP request with the provided http.Client and returns an HTTP response.
|
// Do sends an HTTP request with the provided http.Client and returns an HTTP response.
|
||||||
// If the client is nil, http.DefaultClient is used.
|
// If the client is nil, http.DefaultClient is used.
|
||||||
// If the context is canceled or times out, ctx.Err() will be returned.
|
// If the context is canceled or times out, ctx.Err() will be returned.
|
||||||
|
@ -33,16 +41,44 @@ func Do(ctx context.Context, client *http.Client, req *http.Request) (*http.Resp
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
|
testHookDoReturned()
|
||||||
result <- responseAndError{resp, err}
|
result <- responseAndError{resp, err}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
var resp *http.Response
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
testHookContextDoneBeforeHeaders()
|
||||||
cancel()
|
cancel()
|
||||||
|
// Clean up after the goroutine calling client.Do:
|
||||||
|
go func() {
|
||||||
|
if r := <-result; r.resp != nil {
|
||||||
|
testHookDidBodyClose()
|
||||||
|
r.resp.Body.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
return nil, ctx.Err()
|
return nil, ctx.Err()
|
||||||
case r := <-result:
|
case r := <-result:
|
||||||
return r.resp, r.err
|
var err error
|
||||||
|
resp, err = r.resp, r.err
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
cancel()
|
||||||
|
case <-c:
|
||||||
|
// The response's Body is closed.
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
resp.Body = ¬ifyingReader{resp.Body, c}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get issues a GET request via the Do function.
|
// Get issues a GET request via the Do function.
|
||||||
|
@ -77,3 +113,28 @@ func Post(ctx context.Context, client *http.Client, url string, bodyType string,
|
||||||
func PostForm(ctx context.Context, client *http.Client, url string, data url.Values) (*http.Response, error) {
|
func PostForm(ctx context.Context, client *http.Client, url string, data url.Values) (*http.Response, error) {
|
||||||
return Post(ctx, client, url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
|
return Post(ctx, client, url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// notifyingReader is an io.ReadCloser that closes the notify channel after
|
||||||
|
// Close is called or a Read fails on the underlying ReadCloser.
|
||||||
|
type notifyingReader struct {
|
||||||
|
io.ReadCloser
|
||||||
|
notify chan<- struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *notifyingReader) Read(p []byte) (int, error) {
|
||||||
|
n, err := r.ReadCloser.Read(p)
|
||||||
|
if err != nil && r.notify != nil {
|
||||||
|
close(r.notify)
|
||||||
|
r.notify = nil
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *notifyingReader) Close() error {
|
||||||
|
err := r.ReadCloser.Close()
|
||||||
|
if r.notify != nil {
|
||||||
|
close(r.notify)
|
||||||
|
r.notify = nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
|
@ -17,8 +17,15 @@ RUN apt-get install -y --no-install-recommends \
|
||||||
libcunit1-dev libssl-dev libxml2-dev libevent-dev \
|
libcunit1-dev libssl-dev libxml2-dev libevent-dev \
|
||||||
automake autoconf
|
automake autoconf
|
||||||
|
|
||||||
|
# The list of packages nghttp2 recommends for h2load:
|
||||||
|
RUN apt-get install -y --no-install-recommends make binutils \
|
||||||
|
autoconf automake autotools-dev \
|
||||||
|
libtool pkg-config zlib1g-dev libcunit1-dev libssl-dev libxml2-dev \
|
||||||
|
libev-dev libevent-dev libjansson-dev libjemalloc-dev \
|
||||||
|
cython python3.4-dev python-setuptools
|
||||||
|
|
||||||
# Note: setting NGHTTP2_VER before the git clone, so an old git clone isn't cached:
|
# Note: setting NGHTTP2_VER before the git clone, so an old git clone isn't cached:
|
||||||
ENV NGHTTP2_VER af24f8394e43f4
|
ENV NGHTTP2_VER 895da9a
|
||||||
RUN cd /root && git clone https://github.com/tatsuhiro-t/nghttp2.git
|
RUN cd /root && git clone https://github.com/tatsuhiro-t/nghttp2.git
|
||||||
|
|
||||||
WORKDIR /root/nghttp2
|
WORKDIR /root/nghttp2
|
||||||
|
@ -31,9 +38,9 @@ RUN make
|
||||||
RUN make install
|
RUN make install
|
||||||
|
|
||||||
WORKDIR /root
|
WORKDIR /root
|
||||||
RUN wget http://curl.haxx.se/download/curl-7.40.0.tar.gz
|
RUN wget http://curl.haxx.se/download/curl-7.45.0.tar.gz
|
||||||
RUN tar -zxvf curl-7.40.0.tar.gz
|
RUN tar -zxvf curl-7.45.0.tar.gz
|
||||||
WORKDIR /root/curl-7.40.0
|
WORKDIR /root/curl-7.45.0
|
||||||
RUN ./configure --with-ssl --with-nghttp2=/usr/local
|
RUN ./configure --with-ssl --with-nghttp2=/usr/local
|
||||||
RUN make
|
RUN make
|
||||||
RUN make install
|
RUN make install
|
||||||
|
|
|
@ -17,4 +17,4 @@ Demo test server at https://http2.golang.org/
|
||||||
Help & bug reports welcome!
|
Help & bug reports welcome!
|
||||||
|
|
||||||
Contributing: https://golang.org/doc/contribute.html
|
Contributing: https://golang.org/doc/contribute.html
|
||||||
Bugs: https://github.com/golang/go/issues/new?title=x/net/http2:+
|
Bugs: https://golang.org/issue/new?title=x/net/http2:+
|
||||||
|
|
|
@ -1,76 +0,0 @@
|
||||||
// Copyright 2014 The Go Authors.
|
|
||||||
// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
|
|
||||||
// Licensed under the same terms as Go itself:
|
|
||||||
// https://code.google.com/p/go/source/browse/LICENSE
|
|
||||||
|
|
||||||
package http2
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// buffer is an io.ReadWriteCloser backed by a fixed size buffer.
|
|
||||||
// It never allocates, but moves old data as new data is written.
|
|
||||||
type buffer struct {
|
|
||||||
buf []byte
|
|
||||||
r, w int
|
|
||||||
closed bool
|
|
||||||
err error // err to return to reader
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
errReadEmpty = errors.New("read from empty buffer")
|
|
||||||
errWriteClosed = errors.New("write on closed buffer")
|
|
||||||
errWriteFull = errors.New("write on full buffer")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Read copies bytes from the buffer into p.
|
|
||||||
// It is an error to read when no data is available.
|
|
||||||
func (b *buffer) Read(p []byte) (n int, err error) {
|
|
||||||
n = copy(p, b.buf[b.r:b.w])
|
|
||||||
b.r += n
|
|
||||||
if b.closed && b.r == b.w {
|
|
||||||
err = b.err
|
|
||||||
} else if b.r == b.w && n == 0 {
|
|
||||||
err = errReadEmpty
|
|
||||||
}
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len returns the number of bytes of the unread portion of the buffer.
|
|
||||||
func (b *buffer) Len() int {
|
|
||||||
return b.w - b.r
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write copies bytes from p into the buffer.
|
|
||||||
// It is an error to write more data than the buffer can hold.
|
|
||||||
func (b *buffer) Write(p []byte) (n int, err error) {
|
|
||||||
if b.closed {
|
|
||||||
return 0, errWriteClosed
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slide existing data to beginning.
|
|
||||||
if b.r > 0 && len(p) > len(b.buf)-b.w {
|
|
||||||
copy(b.buf, b.buf[b.r:b.w])
|
|
||||||
b.w -= b.r
|
|
||||||
b.r = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write new data.
|
|
||||||
n = copy(b.buf[b.w:], p)
|
|
||||||
b.w += n
|
|
||||||
if n < len(p) {
|
|
||||||
err = errWriteFull
|
|
||||||
}
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close marks the buffer as closed. Future calls to Write will
|
|
||||||
// return an error. Future calls to Read, once the buffer is
|
|
||||||
// empty, will return err.
|
|
||||||
func (b *buffer) Close(err error) {
|
|
||||||
if !b.closed {
|
|
||||||
b.closed = true
|
|
||||||
b.err = err
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,225 @@
|
||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Transport code's client connection pooling.
|
||||||
|
|
||||||
|
package http2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ClientConnPool manages a pool of HTTP/2 client connections.
|
||||||
|
type ClientConnPool interface {
|
||||||
|
GetClientConn(req *http.Request, addr string) (*ClientConn, error)
|
||||||
|
MarkDead(*ClientConn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: use singleflight for dialing and addConnCalls?
|
||||||
|
type clientConnPool struct {
|
||||||
|
t *Transport
|
||||||
|
|
||||||
|
mu sync.Mutex // TODO: maybe switch to RWMutex
|
||||||
|
// TODO: add support for sharing conns based on cert names
|
||||||
|
// (e.g. share conn for googleapis.com and appspot.com)
|
||||||
|
conns map[string][]*ClientConn // key is host:port
|
||||||
|
dialing map[string]*dialCall // currently in-flight dials
|
||||||
|
keys map[*ClientConn][]string
|
||||||
|
addConnCalls map[string]*addConnCall // in-flight addConnIfNeede calls
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *clientConnPool) GetClientConn(req *http.Request, addr string) (*ClientConn, error) {
|
||||||
|
return p.getClientConn(req, addr, dialOnMiss)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
dialOnMiss = true
|
||||||
|
noDialOnMiss = false
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *clientConnPool) getClientConn(_ *http.Request, addr string, dialOnMiss bool) (*ClientConn, error) {
|
||||||
|
p.mu.Lock()
|
||||||
|
for _, cc := range p.conns[addr] {
|
||||||
|
if cc.CanTakeNewRequest() {
|
||||||
|
p.mu.Unlock()
|
||||||
|
return cc, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !dialOnMiss {
|
||||||
|
p.mu.Unlock()
|
||||||
|
return nil, ErrNoCachedConn
|
||||||
|
}
|
||||||
|
call := p.getStartDialLocked(addr)
|
||||||
|
p.mu.Unlock()
|
||||||
|
<-call.done
|
||||||
|
return call.res, call.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// dialCall is an in-flight Transport dial call to a host.
|
||||||
|
type dialCall struct {
|
||||||
|
p *clientConnPool
|
||||||
|
done chan struct{} // closed when done
|
||||||
|
res *ClientConn // valid after done is closed
|
||||||
|
err error // valid after done is closed
|
||||||
|
}
|
||||||
|
|
||||||
|
// requires p.mu is held.
|
||||||
|
func (p *clientConnPool) getStartDialLocked(addr string) *dialCall {
|
||||||
|
if call, ok := p.dialing[addr]; ok {
|
||||||
|
// A dial is already in-flight. Don't start another.
|
||||||
|
return call
|
||||||
|
}
|
||||||
|
call := &dialCall{p: p, done: make(chan struct{})}
|
||||||
|
if p.dialing == nil {
|
||||||
|
p.dialing = make(map[string]*dialCall)
|
||||||
|
}
|
||||||
|
p.dialing[addr] = call
|
||||||
|
go call.dial(addr)
|
||||||
|
return call
|
||||||
|
}
|
||||||
|
|
||||||
|
// run in its own goroutine.
|
||||||
|
func (c *dialCall) dial(addr string) {
|
||||||
|
c.res, c.err = c.p.t.dialClientConn(addr)
|
||||||
|
close(c.done)
|
||||||
|
|
||||||
|
c.p.mu.Lock()
|
||||||
|
delete(c.p.dialing, addr)
|
||||||
|
if c.err == nil {
|
||||||
|
c.p.addConnLocked(addr, c.res)
|
||||||
|
}
|
||||||
|
c.p.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// addConnIfNeeded makes a NewClientConn out of c if a connection for key doesn't
|
||||||
|
// already exist. It coalesces concurrent calls with the same key.
|
||||||
|
// This is used by the http1 Transport code when it creates a new connection. Because
|
||||||
|
// the http1 Transport doesn't de-dup TCP dials to outbound hosts (because it doesn't know
|
||||||
|
// the protocol), it can get into a situation where it has multiple TLS connections.
|
||||||
|
// This code decides which ones live or die.
|
||||||
|
// The return value used is whether c was used.
|
||||||
|
// c is never closed.
|
||||||
|
func (p *clientConnPool) addConnIfNeeded(key string, t *Transport, c *tls.Conn) (used bool, err error) {
|
||||||
|
p.mu.Lock()
|
||||||
|
for _, cc := range p.conns[key] {
|
||||||
|
if cc.CanTakeNewRequest() {
|
||||||
|
p.mu.Unlock()
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
call, dup := p.addConnCalls[key]
|
||||||
|
if !dup {
|
||||||
|
if p.addConnCalls == nil {
|
||||||
|
p.addConnCalls = make(map[string]*addConnCall)
|
||||||
|
}
|
||||||
|
call = &addConnCall{
|
||||||
|
p: p,
|
||||||
|
done: make(chan struct{}),
|
||||||
|
}
|
||||||
|
p.addConnCalls[key] = call
|
||||||
|
go call.run(t, key, c)
|
||||||
|
}
|
||||||
|
p.mu.Unlock()
|
||||||
|
|
||||||
|
<-call.done
|
||||||
|
if call.err != nil {
|
||||||
|
return false, call.err
|
||||||
|
}
|
||||||
|
return !dup, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type addConnCall struct {
|
||||||
|
p *clientConnPool
|
||||||
|
done chan struct{} // closed when done
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *addConnCall) run(t *Transport, key string, tc *tls.Conn) {
|
||||||
|
cc, err := t.NewClientConn(tc)
|
||||||
|
|
||||||
|
p := c.p
|
||||||
|
p.mu.Lock()
|
||||||
|
if err != nil {
|
||||||
|
c.err = err
|
||||||
|
} else {
|
||||||
|
p.addConnLocked(key, cc)
|
||||||
|
}
|
||||||
|
delete(p.addConnCalls, key)
|
||||||
|
p.mu.Unlock()
|
||||||
|
close(c.done)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *clientConnPool) addConn(key string, cc *ClientConn) {
|
||||||
|
p.mu.Lock()
|
||||||
|
p.addConnLocked(key, cc)
|
||||||
|
p.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// p.mu must be held
|
||||||
|
func (p *clientConnPool) addConnLocked(key string, cc *ClientConn) {
|
||||||
|
for _, v := range p.conns[key] {
|
||||||
|
if v == cc {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.conns == nil {
|
||||||
|
p.conns = make(map[string][]*ClientConn)
|
||||||
|
}
|
||||||
|
if p.keys == nil {
|
||||||
|
p.keys = make(map[*ClientConn][]string)
|
||||||
|
}
|
||||||
|
p.conns[key] = append(p.conns[key], cc)
|
||||||
|
p.keys[cc] = append(p.keys[cc], key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *clientConnPool) MarkDead(cc *ClientConn) {
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
for _, key := range p.keys[cc] {
|
||||||
|
vv, ok := p.conns[key]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newList := filterOutClientConn(vv, cc)
|
||||||
|
if len(newList) > 0 {
|
||||||
|
p.conns[key] = newList
|
||||||
|
} else {
|
||||||
|
delete(p.conns, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete(p.keys, cc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *clientConnPool) closeIdleConnections() {
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
// TODO: don't close a cc if it was just added to the pool
|
||||||
|
// milliseconds ago and has never been used. There's currently
|
||||||
|
// a small race window with the HTTP/1 Transport's integration
|
||||||
|
// where it can add an idle conn just before using it, and
|
||||||
|
// somebody else can concurrently call CloseIdleConns and
|
||||||
|
// break some caller's RoundTrip.
|
||||||
|
for _, vv := range p.conns {
|
||||||
|
for _, cc := range vv {
|
||||||
|
cc.closeIfIdle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterOutClientConn(in []*ClientConn, exclude *ClientConn) []*ClientConn {
|
||||||
|
out := in[:0]
|
||||||
|
for _, v := range in {
|
||||||
|
if v != exclude {
|
||||||
|
out = append(out, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If we filtered it out, zero out the last item to prevent
|
||||||
|
// the GC from seeing it.
|
||||||
|
if len(in) != len(out) {
|
||||||
|
in[len(in)-1] = nil
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build go1.6
|
||||||
|
|
||||||
|
package http2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func configureTransport(t1 *http.Transport) (*Transport, error) {
|
||||||
|
connPool := new(clientConnPool)
|
||||||
|
t2 := &Transport{
|
||||||
|
ConnPool: noDialClientConnPool{connPool},
|
||||||
|
t1: t1,
|
||||||
|
}
|
||||||
|
connPool.t = t2
|
||||||
|
if err := registerHTTPSProtocol(t1, noDialH2RoundTripper{t2}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if t1.TLSClientConfig == nil {
|
||||||
|
t1.TLSClientConfig = new(tls.Config)
|
||||||
|
}
|
||||||
|
if !strSliceContains(t1.TLSClientConfig.NextProtos, "h2") {
|
||||||
|
t1.TLSClientConfig.NextProtos = append([]string{"h2"}, t1.TLSClientConfig.NextProtos...)
|
||||||
|
}
|
||||||
|
if !strSliceContains(t1.TLSClientConfig.NextProtos, "http/1.1") {
|
||||||
|
t1.TLSClientConfig.NextProtos = append(t1.TLSClientConfig.NextProtos, "http/1.1")
|
||||||
|
}
|
||||||
|
upgradeFn := func(authority string, c *tls.Conn) http.RoundTripper {
|
||||||
|
addr := authorityAddr(authority)
|
||||||
|
if used, err := connPool.addConnIfNeeded(addr, t2, c); err != nil {
|
||||||
|
go c.Close()
|
||||||
|
return erringRoundTripper{err}
|
||||||
|
} else if !used {
|
||||||
|
// Turns out we don't need this c.
|
||||||
|
// For example, two goroutines made requests to the same host
|
||||||
|
// at the same time, both kicking off TCP dials. (since protocol
|
||||||
|
// was unknown)
|
||||||
|
go c.Close()
|
||||||
|
}
|
||||||
|
return t2
|
||||||
|
}
|
||||||
|
if m := t1.TLSNextProto; len(m) == 0 {
|
||||||
|
t1.TLSNextProto = map[string]func(string, *tls.Conn) http.RoundTripper{
|
||||||
|
"h2": upgradeFn,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m["h2"] = upgradeFn
|
||||||
|
}
|
||||||
|
return t2, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// registerHTTPSProtocol calls Transport.RegisterProtocol but
|
||||||
|
// convering panics into errors.
|
||||||
|
func registerHTTPSProtocol(t *http.Transport, rt http.RoundTripper) (err error) {
|
||||||
|
defer func() {
|
||||||
|
if e := recover(); e != nil {
|
||||||
|
err = fmt.Errorf("%v", e)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
t.RegisterProtocol("https", rt)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// noDialClientConnPool is an implementation of http2.ClientConnPool
|
||||||
|
// which never dials. We let the HTTP/1.1 client dial and use its TLS
|
||||||
|
// connection instead.
|
||||||
|
type noDialClientConnPool struct{ *clientConnPool }
|
||||||
|
|
||||||
|
func (p noDialClientConnPool) GetClientConn(req *http.Request, addr string) (*ClientConn, error) {
|
||||||
|
return p.getClientConn(req, addr, noDialOnMiss)
|
||||||
|
}
|
||||||
|
|
||||||
|
// noDialH2RoundTripper is a RoundTripper which only tries to complete the request
|
||||||
|
// if there's already has a cached connection to the host.
|
||||||
|
type noDialH2RoundTripper struct{ t *Transport }
|
||||||
|
|
||||||
|
func (rt noDialH2RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
|
res, err := rt.t.RoundTrip(req)
|
||||||
|
if err == ErrNoCachedConn {
|
||||||
|
return nil, http.ErrSkipAltProtocol
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
// Copyright 2014 The Go Authors.
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
|
// Use of this source code is governed by a BSD-style
|
||||||
// Licensed under the same terms as Go itself:
|
// license that can be found in the LICENSE file.
|
||||||
// https://code.google.com/p/go/source/browse/LICENSE
|
|
||||||
|
|
||||||
package http2
|
package http2
|
||||||
|
|
||||||
|
@ -76,3 +75,16 @@ func (e StreamError) Error() string {
|
||||||
type goAwayFlowError struct{}
|
type goAwayFlowError struct{}
|
||||||
|
|
||||||
func (goAwayFlowError) Error() string { return "connection exceeded flow control window size" }
|
func (goAwayFlowError) Error() string { return "connection exceeded flow control window size" }
|
||||||
|
|
||||||
|
// connErrorReason wraps a ConnectionError with an informative error about why it occurs.
|
||||||
|
|
||||||
|
// Errors of this type are only returned by the frame parser functions
|
||||||
|
// and converted into ConnectionError(ErrCodeProtocol).
|
||||||
|
type connError struct {
|
||||||
|
Code ErrCode
|
||||||
|
Reason string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e connError) Error() string {
|
||||||
|
return fmt.Sprintf("http2: connection error: %v: %v", e.Code, e.Reason)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package http2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// fixedBuffer is an io.ReadWriter backed by a fixed size buffer.
|
||||||
|
// It never allocates, but moves old data as new data is written.
|
||||||
|
type fixedBuffer struct {
|
||||||
|
buf []byte
|
||||||
|
r, w int
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
errReadEmpty = errors.New("read from empty fixedBuffer")
|
||||||
|
errWriteFull = errors.New("write on full fixedBuffer")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Read copies bytes from the buffer into p.
|
||||||
|
// It is an error to read when no data is available.
|
||||||
|
func (b *fixedBuffer) Read(p []byte) (n int, err error) {
|
||||||
|
if b.r == b.w {
|
||||||
|
return 0, errReadEmpty
|
||||||
|
}
|
||||||
|
n = copy(p, b.buf[b.r:b.w])
|
||||||
|
b.r += n
|
||||||
|
if b.r == b.w {
|
||||||
|
b.r = 0
|
||||||
|
b.w = 0
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of bytes of the unread portion of the buffer.
|
||||||
|
func (b *fixedBuffer) Len() int {
|
||||||
|
return b.w - b.r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write copies bytes from p into the buffer.
|
||||||
|
// It is an error to write more data than the buffer can hold.
|
||||||
|
func (b *fixedBuffer) Write(p []byte) (n int, err error) {
|
||||||
|
// Slide existing data to beginning.
|
||||||
|
if b.r > 0 && len(p) > len(b.buf)-b.w {
|
||||||
|
copy(b.buf, b.buf[b.r:b.w])
|
||||||
|
b.w -= b.r
|
||||||
|
b.r = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write new data.
|
||||||
|
n = copy(b.buf[b.w:], p)
|
||||||
|
b.w += n
|
||||||
|
if n < len(p) {
|
||||||
|
err = errWriteFull
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
// Copyright 2014 The Go Authors.
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
|
// Use of this source code is governed by a BSD-style
|
||||||
// Licensed under the same terms as Go itself:
|
// license that can be found in the LICENSE file.
|
||||||
// https://code.google.com/p/go/source/browse/LICENSE
|
|
||||||
|
|
||||||
// Flow control
|
// Flow control
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
// Copyright 2014 The Go Authors.
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
|
// Use of this source code is governed by a BSD-style
|
||||||
// Licensed under the same terms as Go itself:
|
// license that can be found in the LICENSE file.
|
||||||
// https://code.google.com/p/go/source/browse/LICENSE
|
|
||||||
|
|
||||||
package http2
|
package http2
|
||||||
|
|
||||||
|
@ -11,6 +10,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -172,6 +172,12 @@ func (h FrameHeader) Header() FrameHeader { return h }
|
||||||
func (h FrameHeader) String() string {
|
func (h FrameHeader) String() string {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
buf.WriteString("[FrameHeader ")
|
buf.WriteString("[FrameHeader ")
|
||||||
|
h.writeDebug(&buf)
|
||||||
|
buf.WriteByte(']')
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h FrameHeader) writeDebug(buf *bytes.Buffer) {
|
||||||
buf.WriteString(h.Type.String())
|
buf.WriteString(h.Type.String())
|
||||||
if h.Flags != 0 {
|
if h.Flags != 0 {
|
||||||
buf.WriteString(" flags=")
|
buf.WriteString(" flags=")
|
||||||
|
@ -188,15 +194,14 @@ func (h FrameHeader) String() string {
|
||||||
if name != "" {
|
if name != "" {
|
||||||
buf.WriteString(name)
|
buf.WriteString(name)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(&buf, "0x%x", 1<<i)
|
fmt.Fprintf(buf, "0x%x", 1<<i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if h.StreamID != 0 {
|
if h.StreamID != 0 {
|
||||||
fmt.Fprintf(&buf, " stream=%d", h.StreamID)
|
fmt.Fprintf(buf, " stream=%d", h.StreamID)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(&buf, " len=%d]", h.Length)
|
fmt.Fprintf(buf, " len=%d", h.Length)
|
||||||
return buf.String()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *FrameHeader) checkValid() {
|
func (h *FrameHeader) checkValid() {
|
||||||
|
@ -256,6 +261,11 @@ type Frame interface {
|
||||||
type Framer struct {
|
type Framer struct {
|
||||||
r io.Reader
|
r io.Reader
|
||||||
lastFrame Frame
|
lastFrame Frame
|
||||||
|
errReason string
|
||||||
|
|
||||||
|
// lastHeaderStream is non-zero if the last frame was an
|
||||||
|
// unfinished HEADERS/CONTINUATION.
|
||||||
|
lastHeaderStream uint32
|
||||||
|
|
||||||
maxReadSize uint32
|
maxReadSize uint32
|
||||||
headerBuf [frameHeaderLen]byte
|
headerBuf [frameHeaderLen]byte
|
||||||
|
@ -272,18 +282,29 @@ type Framer struct {
|
||||||
wbuf []byte
|
wbuf []byte
|
||||||
|
|
||||||
// AllowIllegalWrites permits the Framer's Write methods to
|
// AllowIllegalWrites permits the Framer's Write methods to
|
||||||
// write frames that do not conform to the HTTP/2 spec. This
|
// write frames that do not conform to the HTTP/2 spec. This
|
||||||
// permits using the Framer to test other HTTP/2
|
// permits using the Framer to test other HTTP/2
|
||||||
// implementations' conformance to the spec.
|
// implementations' conformance to the spec.
|
||||||
// If false, the Write methods will prefer to return an error
|
// If false, the Write methods will prefer to return an error
|
||||||
// rather than comply.
|
// rather than comply.
|
||||||
AllowIllegalWrites bool
|
AllowIllegalWrites bool
|
||||||
|
|
||||||
|
// AllowIllegalReads permits the Framer's ReadFrame method
|
||||||
|
// to return non-compliant frames or frame orders.
|
||||||
|
// This is for testing and permits using the Framer to test
|
||||||
|
// other HTTP/2 implementations' conformance to the spec.
|
||||||
|
AllowIllegalReads bool
|
||||||
|
|
||||||
// TODO: track which type of frame & with which flags was sent
|
// TODO: track which type of frame & with which flags was sent
|
||||||
// last. Then return an error (unless AllowIllegalWrites) if
|
// last. Then return an error (unless AllowIllegalWrites) if
|
||||||
// we're in the middle of a header block and a
|
// we're in the middle of a header block and a
|
||||||
// non-Continuation or Continuation on a different stream is
|
// non-Continuation or Continuation on a different stream is
|
||||||
// attempted to be written.
|
// attempted to be written.
|
||||||
|
|
||||||
|
logReads bool
|
||||||
|
|
||||||
|
debugFramer *Framer // only use for logging written writes
|
||||||
|
debugFramerBuf *bytes.Buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Framer) startWrite(ftype FrameType, flags Flags, streamID uint32) {
|
func (f *Framer) startWrite(ftype FrameType, flags Flags, streamID uint32) {
|
||||||
|
@ -311,6 +332,10 @@ func (f *Framer) endWrite() error {
|
||||||
byte(length>>16),
|
byte(length>>16),
|
||||||
byte(length>>8),
|
byte(length>>8),
|
||||||
byte(length))
|
byte(length))
|
||||||
|
if logFrameWrites {
|
||||||
|
f.logWrite()
|
||||||
|
}
|
||||||
|
|
||||||
n, err := f.w.Write(f.wbuf)
|
n, err := f.w.Write(f.wbuf)
|
||||||
if err == nil && n != len(f.wbuf) {
|
if err == nil && n != len(f.wbuf) {
|
||||||
err = io.ErrShortWrite
|
err = io.ErrShortWrite
|
||||||
|
@ -318,6 +343,24 @@ func (f *Framer) endWrite() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *Framer) logWrite() {
|
||||||
|
if f.debugFramer == nil {
|
||||||
|
f.debugFramerBuf = new(bytes.Buffer)
|
||||||
|
f.debugFramer = NewFramer(nil, f.debugFramerBuf)
|
||||||
|
f.debugFramer.logReads = false // we log it ourselves, saying "wrote" below
|
||||||
|
// Let us read anything, even if we accidentally wrote it
|
||||||
|
// in the wrong order:
|
||||||
|
f.debugFramer.AllowIllegalReads = true
|
||||||
|
}
|
||||||
|
f.debugFramerBuf.Write(f.wbuf)
|
||||||
|
fr, err := f.debugFramer.ReadFrame()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("http2: Framer %p: failed to decode just-written frame", f)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("http2: Framer %p: wrote %v", f, summarizeFrame(fr))
|
||||||
|
}
|
||||||
|
|
||||||
func (f *Framer) writeByte(v byte) { f.wbuf = append(f.wbuf, v) }
|
func (f *Framer) writeByte(v byte) { f.wbuf = append(f.wbuf, v) }
|
||||||
func (f *Framer) writeBytes(v []byte) { f.wbuf = append(f.wbuf, v...) }
|
func (f *Framer) writeBytes(v []byte) { f.wbuf = append(f.wbuf, v...) }
|
||||||
func (f *Framer) writeUint16(v uint16) { f.wbuf = append(f.wbuf, byte(v>>8), byte(v)) }
|
func (f *Framer) writeUint16(v uint16) { f.wbuf = append(f.wbuf, byte(v>>8), byte(v)) }
|
||||||
|
@ -333,8 +376,9 @@ const (
|
||||||
// NewFramer returns a Framer that writes frames to w and reads them from r.
|
// NewFramer returns a Framer that writes frames to w and reads them from r.
|
||||||
func NewFramer(w io.Writer, r io.Reader) *Framer {
|
func NewFramer(w io.Writer, r io.Reader) *Framer {
|
||||||
fr := &Framer{
|
fr := &Framer{
|
||||||
w: w,
|
w: w,
|
||||||
r: r,
|
r: r,
|
||||||
|
logReads: logFrameReads,
|
||||||
}
|
}
|
||||||
fr.getReadBuf = func(size uint32) []byte {
|
fr.getReadBuf = func(size uint32) []byte {
|
||||||
if cap(fr.readBuf) >= int(size) {
|
if cap(fr.readBuf) >= int(size) {
|
||||||
|
@ -362,10 +406,22 @@ func (fr *Framer) SetMaxReadFrameSize(v uint32) {
|
||||||
// sends a frame that is larger than declared with SetMaxReadFrameSize.
|
// sends a frame that is larger than declared with SetMaxReadFrameSize.
|
||||||
var ErrFrameTooLarge = errors.New("http2: frame too large")
|
var ErrFrameTooLarge = errors.New("http2: frame too large")
|
||||||
|
|
||||||
|
// terminalReadFrameError reports whether err is an unrecoverable
|
||||||
|
// error from ReadFrame and no other frames should be read.
|
||||||
|
func terminalReadFrameError(err error) bool {
|
||||||
|
if _, ok := err.(StreamError); ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return err != nil
|
||||||
|
}
|
||||||
|
|
||||||
// ReadFrame reads a single frame. The returned Frame is only valid
|
// ReadFrame reads a single frame. The returned Frame is only valid
|
||||||
// until the next call to ReadFrame.
|
// until the next call to ReadFrame.
|
||||||
// If the frame is larger than previously set with SetMaxReadFrameSize,
|
//
|
||||||
// the returned error is ErrFrameTooLarge.
|
// If the frame is larger than previously set with SetMaxReadFrameSize, the
|
||||||
|
// returned error is ErrFrameTooLarge. Other errors may be of type
|
||||||
|
// ConnectionError, StreamError, or anything else from from the underlying
|
||||||
|
// reader.
|
||||||
func (fr *Framer) ReadFrame() (Frame, error) {
|
func (fr *Framer) ReadFrame() (Frame, error) {
|
||||||
if fr.lastFrame != nil {
|
if fr.lastFrame != nil {
|
||||||
fr.lastFrame.invalidate()
|
fr.lastFrame.invalidate()
|
||||||
|
@ -383,12 +439,68 @@ func (fr *Framer) ReadFrame() (Frame, error) {
|
||||||
}
|
}
|
||||||
f, err := typeFrameParser(fh.Type)(fh, payload)
|
f, err := typeFrameParser(fh.Type)(fh, payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if ce, ok := err.(connError); ok {
|
||||||
|
return nil, fr.connError(ce.Code, ce.Reason)
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
fr.lastFrame = f
|
if err := fr.checkFrameOrder(f); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if fr.logReads {
|
||||||
|
log.Printf("http2: Framer %p: read %v", fr, summarizeFrame(f))
|
||||||
|
}
|
||||||
return f, nil
|
return f, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// connError returns ConnectionError(code) but first
|
||||||
|
// stashes away a public reason to the caller can optionally relay it
|
||||||
|
// to the peer before hanging up on them. This might help others debug
|
||||||
|
// their implementations.
|
||||||
|
func (fr *Framer) connError(code ErrCode, reason string) error {
|
||||||
|
fr.errReason = reason
|
||||||
|
return ConnectionError(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkFrameOrder reports an error if f is an invalid frame to return
|
||||||
|
// next from ReadFrame. Mostly it checks whether HEADERS and
|
||||||
|
// CONTINUATION frames are contiguous.
|
||||||
|
func (fr *Framer) checkFrameOrder(f Frame) error {
|
||||||
|
last := fr.lastFrame
|
||||||
|
fr.lastFrame = f
|
||||||
|
if fr.AllowIllegalReads {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fh := f.Header()
|
||||||
|
if fr.lastHeaderStream != 0 {
|
||||||
|
if fh.Type != FrameContinuation {
|
||||||
|
return fr.connError(ErrCodeProtocol,
|
||||||
|
fmt.Sprintf("got %s for stream %d; expected CONTINUATION following %s for stream %d",
|
||||||
|
fh.Type, fh.StreamID,
|
||||||
|
last.Header().Type, fr.lastHeaderStream))
|
||||||
|
}
|
||||||
|
if fh.StreamID != fr.lastHeaderStream {
|
||||||
|
return fr.connError(ErrCodeProtocol,
|
||||||
|
fmt.Sprintf("got CONTINUATION for stream %d; expected stream %d",
|
||||||
|
fh.StreamID, fr.lastHeaderStream))
|
||||||
|
}
|
||||||
|
} else if fh.Type == FrameContinuation {
|
||||||
|
return fr.connError(ErrCodeProtocol, fmt.Sprintf("unexpected CONTINUATION for stream %d", fh.StreamID))
|
||||||
|
}
|
||||||
|
|
||||||
|
switch fh.Type {
|
||||||
|
case FrameHeaders, FrameContinuation:
|
||||||
|
if fh.Flags.Has(FlagHeadersEndHeaders) {
|
||||||
|
fr.lastHeaderStream = 0
|
||||||
|
} else {
|
||||||
|
fr.lastHeaderStream = fh.StreamID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// A DataFrame conveys arbitrary, variable-length sequences of octets
|
// A DataFrame conveys arbitrary, variable-length sequences of octets
|
||||||
// associated with a stream.
|
// associated with a stream.
|
||||||
// See http://http2.github.io/http2-spec/#rfc.section.6.1
|
// See http://http2.github.io/http2-spec/#rfc.section.6.1
|
||||||
|
@ -417,7 +529,7 @@ func parseDataFrame(fh FrameHeader, payload []byte) (Frame, error) {
|
||||||
// field is 0x0, the recipient MUST respond with a
|
// field is 0x0, the recipient MUST respond with a
|
||||||
// connection error (Section 5.4.1) of type
|
// connection error (Section 5.4.1) of type
|
||||||
// PROTOCOL_ERROR.
|
// PROTOCOL_ERROR.
|
||||||
return nil, ConnectionError(ErrCodeProtocol)
|
return nil, connError{ErrCodeProtocol, "DATA frame with stream ID 0"}
|
||||||
}
|
}
|
||||||
f := &DataFrame{
|
f := &DataFrame{
|
||||||
FrameHeader: fh,
|
FrameHeader: fh,
|
||||||
|
@ -435,7 +547,7 @@ func parseDataFrame(fh FrameHeader, payload []byte) (Frame, error) {
|
||||||
// length of the frame payload, the recipient MUST
|
// length of the frame payload, the recipient MUST
|
||||||
// treat this as a connection error.
|
// treat this as a connection error.
|
||||||
// Filed: https://github.com/http2/http2-spec/issues/610
|
// Filed: https://github.com/http2/http2-spec/issues/610
|
||||||
return nil, ConnectionError(ErrCodeProtocol)
|
return nil, connError{ErrCodeProtocol, "pad size larger than data payload"}
|
||||||
}
|
}
|
||||||
f.data = payload[:len(payload)-int(padSize)]
|
f.data = payload[:len(payload)-int(padSize)]
|
||||||
return f, nil
|
return f, nil
|
||||||
|
@ -575,6 +687,8 @@ type PingFrame struct {
|
||||||
Data [8]byte
|
Data [8]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *PingFrame) IsAck() bool { return f.Flags.Has(FlagPingAck) }
|
||||||
|
|
||||||
func parsePingFrame(fh FrameHeader, payload []byte) (Frame, error) {
|
func parsePingFrame(fh FrameHeader, payload []byte) (Frame, error) {
|
||||||
if len(payload) != 8 {
|
if len(payload) != 8 {
|
||||||
return nil, ConnectionError(ErrCodeFrameSize)
|
return nil, ConnectionError(ErrCodeFrameSize)
|
||||||
|
@ -663,7 +777,7 @@ func parseUnknownFrame(fh FrameHeader, p []byte) (Frame, error) {
|
||||||
// See http://http2.github.io/http2-spec/#rfc.section.6.9
|
// See http://http2.github.io/http2-spec/#rfc.section.6.9
|
||||||
type WindowUpdateFrame struct {
|
type WindowUpdateFrame struct {
|
||||||
FrameHeader
|
FrameHeader
|
||||||
Increment uint32
|
Increment uint32 // never read with high bit set
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseWindowUpdateFrame(fh FrameHeader, p []byte) (Frame, error) {
|
func parseWindowUpdateFrame(fh FrameHeader, p []byte) (Frame, error) {
|
||||||
|
@ -740,7 +854,7 @@ func parseHeadersFrame(fh FrameHeader, p []byte) (_ Frame, err error) {
|
||||||
// is received whose stream identifier field is 0x0, the recipient MUST
|
// is received whose stream identifier field is 0x0, the recipient MUST
|
||||||
// respond with a connection error (Section 5.4.1) of type
|
// respond with a connection error (Section 5.4.1) of type
|
||||||
// PROTOCOL_ERROR.
|
// PROTOCOL_ERROR.
|
||||||
return nil, ConnectionError(ErrCodeProtocol)
|
return nil, connError{ErrCodeProtocol, "HEADERS frame with stream ID 0"}
|
||||||
}
|
}
|
||||||
var padLength uint8
|
var padLength uint8
|
||||||
if fh.Flags.Has(FlagHeadersPadded) {
|
if fh.Flags.Has(FlagHeadersPadded) {
|
||||||
|
@ -870,10 +984,10 @@ func (p PriorityParam) IsZero() bool {
|
||||||
|
|
||||||
func parsePriorityFrame(fh FrameHeader, payload []byte) (Frame, error) {
|
func parsePriorityFrame(fh FrameHeader, payload []byte) (Frame, error) {
|
||||||
if fh.StreamID == 0 {
|
if fh.StreamID == 0 {
|
||||||
return nil, ConnectionError(ErrCodeProtocol)
|
return nil, connError{ErrCodeProtocol, "PRIORITY frame with stream ID 0"}
|
||||||
}
|
}
|
||||||
if len(payload) != 5 {
|
if len(payload) != 5 {
|
||||||
return nil, ConnectionError(ErrCodeFrameSize)
|
return nil, connError{ErrCodeFrameSize, fmt.Sprintf("PRIORITY frame payload size was %d; want 5", len(payload))}
|
||||||
}
|
}
|
||||||
v := binary.BigEndian.Uint32(payload[:4])
|
v := binary.BigEndian.Uint32(payload[:4])
|
||||||
streamID := v & 0x7fffffff // mask off high bit
|
streamID := v & 0x7fffffff // mask off high bit
|
||||||
|
@ -943,13 +1057,12 @@ type ContinuationFrame struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseContinuationFrame(fh FrameHeader, p []byte) (Frame, error) {
|
func parseContinuationFrame(fh FrameHeader, p []byte) (Frame, error) {
|
||||||
|
if fh.StreamID == 0 {
|
||||||
|
return nil, connError{ErrCodeProtocol, "CONTINUATION frame with stream ID 0"}
|
||||||
|
}
|
||||||
return &ContinuationFrame{fh, p}, nil
|
return &ContinuationFrame{fh, p}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *ContinuationFrame) StreamEnded() bool {
|
|
||||||
return f.FrameHeader.Flags.Has(FlagDataEndStream)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *ContinuationFrame) HeaderBlockFragment() []byte {
|
func (f *ContinuationFrame) HeaderBlockFragment() []byte {
|
||||||
f.checkValid()
|
f.checkValid()
|
||||||
return f.headerFragBuf
|
return f.headerFragBuf
|
||||||
|
@ -1111,3 +1224,46 @@ type streamEnder interface {
|
||||||
type headersEnder interface {
|
type headersEnder interface {
|
||||||
HeadersEnded() bool
|
HeadersEnded() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func summarizeFrame(f Frame) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
f.Header().writeDebug(&buf)
|
||||||
|
switch f := f.(type) {
|
||||||
|
case *SettingsFrame:
|
||||||
|
n := 0
|
||||||
|
f.ForeachSetting(func(s Setting) error {
|
||||||
|
n++
|
||||||
|
if n == 1 {
|
||||||
|
buf.WriteString(", settings:")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(&buf, " %v=%v,", s.ID, s.Val)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if n > 0 {
|
||||||
|
buf.Truncate(buf.Len() - 1) // remove trailing comma
|
||||||
|
}
|
||||||
|
case *DataFrame:
|
||||||
|
data := f.Data()
|
||||||
|
const max = 256
|
||||||
|
if len(data) > max {
|
||||||
|
data = data[:max]
|
||||||
|
}
|
||||||
|
fmt.Fprintf(&buf, " data=%q", data)
|
||||||
|
if len(f.Data()) > max {
|
||||||
|
fmt.Fprintf(&buf, " (%d bytes omitted)", len(f.Data())-max)
|
||||||
|
}
|
||||||
|
case *WindowUpdateFrame:
|
||||||
|
if f.StreamID == 0 {
|
||||||
|
buf.WriteString(" (conn)")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(&buf, " incr=%v", f.Increment)
|
||||||
|
case *PingFrame:
|
||||||
|
fmt.Fprintf(&buf, " ping=%q", f.Data[:])
|
||||||
|
case *GoAwayFrame:
|
||||||
|
fmt.Fprintf(&buf, " LastStreamID=%v ErrCode=%v Debug=%q",
|
||||||
|
f.LastStreamID, f.ErrCode, f.debugData)
|
||||||
|
case *RSTStreamFrame:
|
||||||
|
fmt.Fprintf(&buf, " ErrCode=%v", f.ErrCode)
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build go1.5
|
||||||
|
|
||||||
|
package http2
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
func requestCancel(req *http.Request) <-chan struct{} { return req.Cancel }
|
|
@ -1,9 +1,6 @@
|
||||||
// Copyright 2014 The Go Authors. All rights reserved.
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
|
|
||||||
// Licensed under the same terms as Go itself:
|
|
||||||
// https://code.google.com/p/go/source/browse/LICENSE
|
|
||||||
|
|
||||||
// Defensive debug-only utility to track that functions run on the
|
// Defensive debug-only utility to track that functions run on the
|
||||||
// goroutine that they're supposed to.
|
// goroutine that they're supposed to.
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
// Copyright 2014 The Go Authors. All rights reserved.
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
|
|
||||||
// Licensed under the same terms as Go itself:
|
|
||||||
// https://code.google.com/p/go/source/browse/LICENSE
|
|
||||||
|
|
||||||
package http2
|
package http2
|
||||||
|
|
||||||
|
@ -60,6 +57,7 @@ func init() {
|
||||||
"server",
|
"server",
|
||||||
"set-cookie",
|
"set-cookie",
|
||||||
"strict-transport-security",
|
"strict-transport-security",
|
||||||
|
"trailer",
|
||||||
"transfer-encoding",
|
"transfer-encoding",
|
||||||
"user-agent",
|
"user-agent",
|
||||||
"vary",
|
"vary",
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
// Copyright 2014 The Go Authors.
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
|
// Use of this source code is governed by a BSD-style
|
||||||
// Licensed under the same terms as Go itself:
|
// license that can be found in the LICENSE file.
|
||||||
// https://code.google.com/p/go/source/browse/LICENSE
|
|
||||||
|
|
||||||
package hpack
|
package hpack
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
// Copyright 2014 The Go Authors.
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
|
// Use of this source code is governed by a BSD-style
|
||||||
// Licensed under the same terms as Go itself:
|
// license that can be found in the LICENSE file.
|
||||||
// https://code.google.com/p/go/source/browse/LICENSE
|
|
||||||
|
|
||||||
// Package hpack implements HPACK, a compression format for
|
// Package hpack implements HPACK, a compression format for
|
||||||
// efficiently representing HTTP header fields in the context of HTTP/2.
|
// efficiently representing HTTP header fields in the context of HTTP/2.
|
||||||
|
@ -42,6 +41,14 @@ type HeaderField struct {
|
||||||
Sensitive bool
|
Sensitive bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (hf HeaderField) String() string {
|
||||||
|
var suffix string
|
||||||
|
if hf.Sensitive {
|
||||||
|
suffix = " (sensitive)"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("header field %q = %q%s", hf.Name, hf.Value, suffix)
|
||||||
|
}
|
||||||
|
|
||||||
func (hf *HeaderField) size() uint32 {
|
func (hf *HeaderField) size() uint32 {
|
||||||
// http://http2.github.io/http2-spec/compression.html#rfc.section.4.1
|
// http://http2.github.io/http2-spec/compression.html#rfc.section.4.1
|
||||||
// "The size of the dynamic table is the sum of the size of
|
// "The size of the dynamic table is the sum of the size of
|
||||||
|
@ -64,23 +71,65 @@ type Decoder struct {
|
||||||
dynTab dynamicTable
|
dynTab dynamicTable
|
||||||
emit func(f HeaderField)
|
emit func(f HeaderField)
|
||||||
|
|
||||||
|
emitEnabled bool // whether calls to emit are enabled
|
||||||
|
maxStrLen int // 0 means unlimited
|
||||||
|
|
||||||
// buf is the unparsed buffer. It's only written to
|
// buf is the unparsed buffer. It's only written to
|
||||||
// saveBuf if it was truncated in the middle of a header
|
// saveBuf if it was truncated in the middle of a header
|
||||||
// block. Because it's usually not owned, we can only
|
// block. Because it's usually not owned, we can only
|
||||||
// process it under Write.
|
// process it under Write.
|
||||||
buf []byte // usually not owned
|
buf []byte // not owned; only valid during Write
|
||||||
|
|
||||||
|
// saveBuf is previous data passed to Write which we weren't able
|
||||||
|
// to fully parse before. Unlike buf, we own this data.
|
||||||
saveBuf bytes.Buffer
|
saveBuf bytes.Buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDecoder(maxSize uint32, emitFunc func(f HeaderField)) *Decoder {
|
// NewDecoder returns a new decoder with the provided maximum dynamic
|
||||||
|
// table size. The emitFunc will be called for each valid field
|
||||||
|
// parsed, in the same goroutine as calls to Write, before Write returns.
|
||||||
|
func NewDecoder(maxDynamicTableSize uint32, emitFunc func(f HeaderField)) *Decoder {
|
||||||
d := &Decoder{
|
d := &Decoder{
|
||||||
emit: emitFunc,
|
emit: emitFunc,
|
||||||
|
emitEnabled: true,
|
||||||
}
|
}
|
||||||
d.dynTab.allowedMaxSize = maxSize
|
d.dynTab.allowedMaxSize = maxDynamicTableSize
|
||||||
d.dynTab.setMaxSize(maxSize)
|
d.dynTab.setMaxSize(maxDynamicTableSize)
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrStringLength is returned by Decoder.Write when the max string length
|
||||||
|
// (as configured by Decoder.SetMaxStringLength) would be violated.
|
||||||
|
var ErrStringLength = errors.New("hpack: string too long")
|
||||||
|
|
||||||
|
// SetMaxStringLength sets the maximum size of a HeaderField name or
|
||||||
|
// value string. If a string exceeds this length (even after any
|
||||||
|
// decompression), Write will return ErrStringLength.
|
||||||
|
// A value of 0 means unlimited and is the default from NewDecoder.
|
||||||
|
func (d *Decoder) SetMaxStringLength(n int) {
|
||||||
|
d.maxStrLen = n
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEmitFunc changes the callback used when new header fields
|
||||||
|
// are decoded.
|
||||||
|
// It must be non-nil. It does not affect EmitEnabled.
|
||||||
|
func (d *Decoder) SetEmitFunc(emitFunc func(f HeaderField)) {
|
||||||
|
d.emit = emitFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEmitEnabled controls whether the emitFunc provided to NewDecoder
|
||||||
|
// should be called. The default is true.
|
||||||
|
//
|
||||||
|
// This facility exists to let servers enforce MAX_HEADER_LIST_SIZE
|
||||||
|
// while still decoding and keeping in-sync with decoder state, but
|
||||||
|
// without doing unnecessary decompression or generating unnecessary
|
||||||
|
// garbage for header fields past the limit.
|
||||||
|
func (d *Decoder) SetEmitEnabled(v bool) { d.emitEnabled = v }
|
||||||
|
|
||||||
|
// EmitEnabled reports whether calls to the emitFunc provided to NewDecoder
|
||||||
|
// are currently enabled. The default is true.
|
||||||
|
func (d *Decoder) EmitEnabled() bool { return d.emitEnabled }
|
||||||
|
|
||||||
// TODO: add method *Decoder.Reset(maxSize, emitFunc) to let callers re-use Decoders and their
|
// TODO: add method *Decoder.Reset(maxSize, emitFunc) to let callers re-use Decoders and their
|
||||||
// underlying buffers for garbage reasons.
|
// underlying buffers for garbage reasons.
|
||||||
|
|
||||||
|
@ -247,15 +296,23 @@ func (d *Decoder) Write(p []byte) (n int, err error) {
|
||||||
|
|
||||||
for len(d.buf) > 0 {
|
for len(d.buf) > 0 {
|
||||||
err = d.parseHeaderFieldRepr()
|
err = d.parseHeaderFieldRepr()
|
||||||
if err != nil {
|
if err == errNeedMore {
|
||||||
if err == errNeedMore {
|
// Extra paranoia, making sure saveBuf won't
|
||||||
err = nil
|
// get too large. All the varint and string
|
||||||
d.saveBuf.Write(d.buf)
|
// reading code earlier should already catch
|
||||||
|
// overlong things and return ErrStringLength,
|
||||||
|
// but keep this as a last resort.
|
||||||
|
const varIntOverhead = 8 // conservative
|
||||||
|
if d.maxStrLen != 0 && int64(len(d.buf)) > 2*(int64(d.maxStrLen)+varIntOverhead) {
|
||||||
|
return 0, ErrStringLength
|
||||||
}
|
}
|
||||||
|
d.saveBuf.Write(d.buf)
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return len(p), err
|
return len(p), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,9 +380,8 @@ func (d *Decoder) parseFieldIndexed() error {
|
||||||
if !ok {
|
if !ok {
|
||||||
return DecodingError{InvalidIndexError(idx)}
|
return DecodingError{InvalidIndexError(idx)}
|
||||||
}
|
}
|
||||||
d.emit(HeaderField{Name: hf.Name, Value: hf.Value})
|
|
||||||
d.buf = buf
|
d.buf = buf
|
||||||
return nil
|
return d.callEmit(HeaderField{Name: hf.Name, Value: hf.Value})
|
||||||
}
|
}
|
||||||
|
|
||||||
// (same invariants and behavior as parseHeaderFieldRepr)
|
// (same invariants and behavior as parseHeaderFieldRepr)
|
||||||
|
@ -337,6 +393,7 @@ func (d *Decoder) parseFieldLiteral(n uint8, it indexType) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
var hf HeaderField
|
var hf HeaderField
|
||||||
|
wantStr := d.emitEnabled || it.indexed()
|
||||||
if nameIdx > 0 {
|
if nameIdx > 0 {
|
||||||
ihf, ok := d.at(nameIdx)
|
ihf, ok := d.at(nameIdx)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -344,12 +401,12 @@ func (d *Decoder) parseFieldLiteral(n uint8, it indexType) error {
|
||||||
}
|
}
|
||||||
hf.Name = ihf.Name
|
hf.Name = ihf.Name
|
||||||
} else {
|
} else {
|
||||||
hf.Name, buf, err = readString(buf)
|
hf.Name, buf, err = d.readString(buf, wantStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hf.Value, buf, err = readString(buf)
|
hf.Value, buf, err = d.readString(buf, wantStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -358,7 +415,18 @@ func (d *Decoder) parseFieldLiteral(n uint8, it indexType) error {
|
||||||
d.dynTab.add(hf)
|
d.dynTab.add(hf)
|
||||||
}
|
}
|
||||||
hf.Sensitive = it.sensitive()
|
hf.Sensitive = it.sensitive()
|
||||||
d.emit(hf)
|
return d.callEmit(hf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) callEmit(hf HeaderField) error {
|
||||||
|
if d.maxStrLen != 0 {
|
||||||
|
if len(hf.Name) > d.maxStrLen || len(hf.Value) > d.maxStrLen {
|
||||||
|
return ErrStringLength
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if d.emitEnabled {
|
||||||
|
d.emit(hf)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -420,7 +488,15 @@ func readVarInt(n byte, p []byte) (i uint64, remain []byte, err error) {
|
||||||
return 0, origP, errNeedMore
|
return 0, origP, errNeedMore
|
||||||
}
|
}
|
||||||
|
|
||||||
func readString(p []byte) (s string, remain []byte, err error) {
|
// readString decodes an hpack string from p.
|
||||||
|
//
|
||||||
|
// wantStr is whether s will be used. If false, decompression and
|
||||||
|
// []byte->string garbage are skipped if s will be ignored
|
||||||
|
// anyway. This does mean that huffman decoding errors for non-indexed
|
||||||
|
// strings past the MAX_HEADER_LIST_SIZE are ignored, but the server
|
||||||
|
// is returning an error anyway, and because they're not indexed, the error
|
||||||
|
// won't affect the decoding state.
|
||||||
|
func (d *Decoder) readString(p []byte, wantStr bool) (s string, remain []byte, err error) {
|
||||||
if len(p) == 0 {
|
if len(p) == 0 {
|
||||||
return "", p, errNeedMore
|
return "", p, errNeedMore
|
||||||
}
|
}
|
||||||
|
@ -429,17 +505,29 @@ func readString(p []byte) (s string, remain []byte, err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", p, err
|
return "", p, err
|
||||||
}
|
}
|
||||||
|
if d.maxStrLen != 0 && strLen > uint64(d.maxStrLen) {
|
||||||
|
return "", nil, ErrStringLength
|
||||||
|
}
|
||||||
if uint64(len(p)) < strLen {
|
if uint64(len(p)) < strLen {
|
||||||
return "", p, errNeedMore
|
return "", p, errNeedMore
|
||||||
}
|
}
|
||||||
if !isHuff {
|
if !isHuff {
|
||||||
return string(p[:strLen]), p[strLen:], nil
|
if wantStr {
|
||||||
|
s = string(p[:strLen])
|
||||||
|
}
|
||||||
|
return s, p[strLen:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: optimize this garbage:
|
if wantStr {
|
||||||
var buf bytes.Buffer
|
buf := bufPool.Get().(*bytes.Buffer)
|
||||||
if _, err := HuffmanDecode(&buf, p[:strLen]); err != nil {
|
buf.Reset() // don't trust others
|
||||||
return "", nil, err
|
defer bufPool.Put(buf)
|
||||||
|
if err := huffmanDecode(buf, d.maxStrLen, p[:strLen]); err != nil {
|
||||||
|
buf.Reset()
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
s = buf.String()
|
||||||
|
buf.Reset() // be nice to GC
|
||||||
}
|
}
|
||||||
return buf.String(), p[strLen:], nil
|
return s, p[strLen:], nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
// Copyright 2014 The Go Authors.
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
|
// Use of this source code is governed by a BSD-style
|
||||||
// Licensed under the same terms as Go itself:
|
// license that can be found in the LICENSE file.
|
||||||
// https://code.google.com/p/go/source/browse/LICENSE
|
|
||||||
|
|
||||||
package hpack
|
package hpack
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
@ -22,15 +22,46 @@ func HuffmanDecode(w io.Writer, v []byte) (int, error) {
|
||||||
buf := bufPool.Get().(*bytes.Buffer)
|
buf := bufPool.Get().(*bytes.Buffer)
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
defer bufPool.Put(buf)
|
defer bufPool.Put(buf)
|
||||||
|
if err := huffmanDecode(buf, 0, v); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return w.Write(buf.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
// HuffmanDecodeToString decodes the string in v.
|
||||||
|
func HuffmanDecodeToString(v []byte) (string, error) {
|
||||||
|
buf := bufPool.Get().(*bytes.Buffer)
|
||||||
|
buf.Reset()
|
||||||
|
defer bufPool.Put(buf)
|
||||||
|
if err := huffmanDecode(buf, 0, v); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return buf.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrInvalidHuffman is returned for errors found decoding
|
||||||
|
// Huffman-encoded strings.
|
||||||
|
var ErrInvalidHuffman = errors.New("hpack: invalid Huffman-encoded data")
|
||||||
|
|
||||||
|
// huffmanDecode decodes v to buf.
|
||||||
|
// If maxLen is greater than 0, attempts to write more to buf than
|
||||||
|
// maxLen bytes will return ErrStringLength.
|
||||||
|
func huffmanDecode(buf *bytes.Buffer, maxLen int, v []byte) error {
|
||||||
n := rootHuffmanNode
|
n := rootHuffmanNode
|
||||||
cur, nbits := uint(0), uint8(0)
|
cur, nbits := uint(0), uint8(0)
|
||||||
for _, b := range v {
|
for _, b := range v {
|
||||||
cur = cur<<8 | uint(b)
|
cur = cur<<8 | uint(b)
|
||||||
nbits += 8
|
nbits += 8
|
||||||
for nbits >= 8 {
|
for nbits >= 8 {
|
||||||
n = n.children[byte(cur>>(nbits-8))]
|
idx := byte(cur >> (nbits - 8))
|
||||||
|
n = n.children[idx]
|
||||||
|
if n == nil {
|
||||||
|
return ErrInvalidHuffman
|
||||||
|
}
|
||||||
if n.children == nil {
|
if n.children == nil {
|
||||||
|
if maxLen != 0 && buf.Len() == maxLen {
|
||||||
|
return ErrStringLength
|
||||||
|
}
|
||||||
buf.WriteByte(n.sym)
|
buf.WriteByte(n.sym)
|
||||||
nbits -= n.codeLen
|
nbits -= n.codeLen
|
||||||
n = rootHuffmanNode
|
n = rootHuffmanNode
|
||||||
|
@ -48,7 +79,7 @@ func HuffmanDecode(w io.Writer, v []byte) (int, error) {
|
||||||
nbits -= n.codeLen
|
nbits -= n.codeLen
|
||||||
n = rootHuffmanNode
|
n = rootHuffmanNode
|
||||||
}
|
}
|
||||||
return w.Write(buf.Bytes())
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type node struct {
|
type node struct {
|
||||||
|
@ -67,10 +98,10 @@ func newInternalNode() *node {
|
||||||
var rootHuffmanNode = newInternalNode()
|
var rootHuffmanNode = newInternalNode()
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
if len(huffmanCodes) != 256 {
|
||||||
|
panic("unexpected size")
|
||||||
|
}
|
||||||
for i, code := range huffmanCodes {
|
for i, code := range huffmanCodes {
|
||||||
if i > 255 {
|
|
||||||
panic("too many huffman codes")
|
|
||||||
}
|
|
||||||
addDecoderNode(byte(i), code, huffmanCodeLen[i])
|
addDecoderNode(byte(i), code, huffmanCodeLen[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
// Copyright 2014 The Go Authors.
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
|
// Use of this source code is governed by a BSD-style
|
||||||
// Licensed under the same terms as Go itself:
|
// license that can be found in the LICENSE file.
|
||||||
// https://code.google.com/p/go/source/browse/LICENSE
|
|
||||||
|
|
||||||
package hpack
|
package hpack
|
||||||
|
|
||||||
|
@ -10,7 +9,7 @@ func pair(name, value string) HeaderField {
|
||||||
}
|
}
|
||||||
|
|
||||||
// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-07#appendix-B
|
// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-07#appendix-B
|
||||||
var staticTable = []HeaderField{
|
var staticTable = [...]HeaderField{
|
||||||
pair(":authority", ""), // index 1 (1-based)
|
pair(":authority", ""), // index 1 (1-based)
|
||||||
pair(":method", "GET"),
|
pair(":method", "GET"),
|
||||||
pair(":method", "POST"),
|
pair(":method", "POST"),
|
||||||
|
@ -74,7 +73,7 @@ var staticTable = []HeaderField{
|
||||||
pair("www-authenticate", ""),
|
pair("www-authenticate", ""),
|
||||||
}
|
}
|
||||||
|
|
||||||
var huffmanCodes = []uint32{
|
var huffmanCodes = [256]uint32{
|
||||||
0x1ff8,
|
0x1ff8,
|
||||||
0x7fffd8,
|
0x7fffd8,
|
||||||
0xfffffe2,
|
0xfffffe2,
|
||||||
|
@ -333,7 +332,7 @@ var huffmanCodes = []uint32{
|
||||||
0x3ffffee,
|
0x3ffffee,
|
||||||
}
|
}
|
||||||
|
|
||||||
var huffmanCodeLen = []uint8{
|
var huffmanCodeLen = [256]uint8{
|
||||||
13, 23, 28, 28, 28, 28, 28, 28, 28, 24, 30, 28, 28, 30, 28, 28,
|
13, 23, 28, 28, 28, 28, 28, 28, 28, 24, 30, 28, 28, 30, 28, 28,
|
||||||
28, 28, 28, 28, 28, 28, 30, 28, 28, 28, 28, 28, 28, 28, 28, 28,
|
28, 28, 28, 28, 28, 28, 30, 28, 28, 28, 28, 28, 28, 28, 28, 28,
|
||||||
6, 10, 10, 12, 13, 6, 8, 11, 10, 10, 8, 11, 8, 6, 6, 6,
|
6, 10, 10, 12, 13, 6, 8, 11, 10, 10, 8, 11, 8, 6, 6, 6,
|
||||||
|
|
|
@ -1,31 +1,50 @@
|
||||||
// Copyright 2014 The Go Authors. All rights reserved.
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
|
|
||||||
// Licensed under the same terms as Go itself:
|
|
||||||
// https://code.google.com/p/go/source/browse/LICENSE
|
|
||||||
|
|
||||||
// Package http2 implements the HTTP/2 protocol.
|
// Package http2 implements the HTTP/2 protocol.
|
||||||
//
|
//
|
||||||
// This is a work in progress. This package is low-level and intended
|
// This package is low-level and intended to be used directly by very
|
||||||
// to be used directly by very few people. Most users will use it
|
// few people. Most users will use it indirectly through the automatic
|
||||||
// indirectly through integration with the net/http package. See
|
// use by the net/http package (from Go 1.6 and later).
|
||||||
// ConfigureServer. That ConfigureServer call will likely be automatic
|
// For use in earlier Go versions see ConfigureServer. (Transport support
|
||||||
// or available via an empty import in the future.
|
// requires Go 1.6 or later)
|
||||||
//
|
//
|
||||||
// See http://http2.github.io/
|
// See https://http2.github.io/ for more information on HTTP/2.
|
||||||
|
//
|
||||||
|
// See https://http2.golang.org/ for a test server running this code.
|
||||||
package http2
|
package http2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
var VerboseLogs = false
|
var (
|
||||||
|
VerboseLogs bool
|
||||||
|
logFrameWrites bool
|
||||||
|
logFrameReads bool
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
e := os.Getenv("GODEBUG")
|
||||||
|
if strings.Contains(e, "http2debug=1") {
|
||||||
|
VerboseLogs = true
|
||||||
|
}
|
||||||
|
if strings.Contains(e, "http2debug=2") {
|
||||||
|
VerboseLogs = true
|
||||||
|
logFrameWrites = true
|
||||||
|
logFrameReads = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// ClientPreface is the string that must be sent by new
|
// ClientPreface is the string that must be sent by new
|
||||||
|
@ -141,17 +160,62 @@ func (s SettingID) String() string {
|
||||||
return fmt.Sprintf("UNKNOWN_SETTING_%d", uint16(s))
|
return fmt.Sprintf("UNKNOWN_SETTING_%d", uint16(s))
|
||||||
}
|
}
|
||||||
|
|
||||||
func validHeader(v string) bool {
|
var (
|
||||||
|
errInvalidHeaderFieldName = errors.New("http2: invalid header field name")
|
||||||
|
errInvalidHeaderFieldValue = errors.New("http2: invalid header field value")
|
||||||
|
)
|
||||||
|
|
||||||
|
// validHeaderFieldName reports whether v is a valid header field name (key).
|
||||||
|
// RFC 7230 says:
|
||||||
|
// header-field = field-name ":" OWS field-value OWS
|
||||||
|
// field-name = token
|
||||||
|
// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." /
|
||||||
|
// "^" / "_" / "
|
||||||
|
// Further, http2 says:
|
||||||
|
// "Just as in HTTP/1.x, header field names are strings of ASCII
|
||||||
|
// characters that are compared in a case-insensitive
|
||||||
|
// fashion. However, header field names MUST be converted to
|
||||||
|
// lowercase prior to their encoding in HTTP/2. "
|
||||||
|
func validHeaderFieldName(v string) bool {
|
||||||
if len(v) == 0 {
|
if len(v) == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for _, r := range v {
|
for _, r := range v {
|
||||||
// "Just as in HTTP/1.x, header field names are
|
if int(r) >= len(isTokenTable) || ('A' <= r && r <= 'Z') {
|
||||||
// strings of ASCII characters that are compared in a
|
return false
|
||||||
// case-insensitive fashion. However, header field
|
}
|
||||||
// names MUST be converted to lowercase prior to their
|
if !isTokenTable[byte(r)] {
|
||||||
// encoding in HTTP/2. "
|
return false
|
||||||
if r >= 127 || ('A' <= r && r <= 'Z') {
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// validHeaderFieldValue reports whether v is a valid header field value.
|
||||||
|
//
|
||||||
|
// RFC 7230 says:
|
||||||
|
// field-value = *( field-content / obs-fold )
|
||||||
|
// obj-fold = N/A to http2, and deprecated
|
||||||
|
// field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
|
||||||
|
// field-vchar = VCHAR / obs-text
|
||||||
|
// obs-text = %x80-FF
|
||||||
|
// VCHAR = "any visible [USASCII] character"
|
||||||
|
//
|
||||||
|
// http2 further says: "Similarly, HTTP/2 allows header field values
|
||||||
|
// that are not valid. While most of the values that can be encoded
|
||||||
|
// will not alter header field parsing, carriage return (CR, ASCII
|
||||||
|
// 0xd), line feed (LF, ASCII 0xa), and the zero character (NUL, ASCII
|
||||||
|
// 0x0) might be exploited by an attacker if they are translated
|
||||||
|
// verbatim. Any request or response that contains a character not
|
||||||
|
// permitted in a header field value MUST be treated as malformed
|
||||||
|
// (Section 8.1.2.6). Valid characters are defined by the
|
||||||
|
// field-content ABNF rule in Section 3.2 of [RFC7230]."
|
||||||
|
//
|
||||||
|
// This function does not (yet?) properly handle the rejection of
|
||||||
|
// strings that begin or end with SP or HTAB.
|
||||||
|
func validHeaderFieldValue(v string) bool {
|
||||||
|
for i := 0; i < len(v); i++ {
|
||||||
|
if b := v[i]; b < ' ' && b != '\t' || b == 0x7f {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -247,3 +311,119 @@ func (w *bufferedWriter) Flush() error {
|
||||||
w.bw = nil
|
w.bw = nil
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mustUint31(v int32) uint32 {
|
||||||
|
if v < 0 || v > 2147483647 {
|
||||||
|
panic("out of range")
|
||||||
|
}
|
||||||
|
return uint32(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// bodyAllowedForStatus reports whether a given response status code
|
||||||
|
// permits a body. See RFC2616, section 4.4.
|
||||||
|
func bodyAllowedForStatus(status int) bool {
|
||||||
|
switch {
|
||||||
|
case status >= 100 && status <= 199:
|
||||||
|
return false
|
||||||
|
case status == 204:
|
||||||
|
return false
|
||||||
|
case status == 304:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type httpError struct {
|
||||||
|
msg string
|
||||||
|
timeout bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *httpError) Error() string { return e.msg }
|
||||||
|
func (e *httpError) Timeout() bool { return e.timeout }
|
||||||
|
func (e *httpError) Temporary() bool { return true }
|
||||||
|
|
||||||
|
var errTimeout error = &httpError{msg: "http2: timeout awaiting response headers", timeout: true}
|
||||||
|
|
||||||
|
var isTokenTable = [127]bool{
|
||||||
|
'!': true,
|
||||||
|
'#': true,
|
||||||
|
'$': true,
|
||||||
|
'%': true,
|
||||||
|
'&': true,
|
||||||
|
'\'': true,
|
||||||
|
'*': true,
|
||||||
|
'+': true,
|
||||||
|
'-': true,
|
||||||
|
'.': true,
|
||||||
|
'0': true,
|
||||||
|
'1': true,
|
||||||
|
'2': true,
|
||||||
|
'3': true,
|
||||||
|
'4': true,
|
||||||
|
'5': true,
|
||||||
|
'6': true,
|
||||||
|
'7': true,
|
||||||
|
'8': true,
|
||||||
|
'9': true,
|
||||||
|
'A': true,
|
||||||
|
'B': true,
|
||||||
|
'C': true,
|
||||||
|
'D': true,
|
||||||
|
'E': true,
|
||||||
|
'F': true,
|
||||||
|
'G': true,
|
||||||
|
'H': true,
|
||||||
|
'I': true,
|
||||||
|
'J': true,
|
||||||
|
'K': true,
|
||||||
|
'L': true,
|
||||||
|
'M': true,
|
||||||
|
'N': true,
|
||||||
|
'O': true,
|
||||||
|
'P': true,
|
||||||
|
'Q': true,
|
||||||
|
'R': true,
|
||||||
|
'S': true,
|
||||||
|
'T': true,
|
||||||
|
'U': true,
|
||||||
|
'W': true,
|
||||||
|
'V': true,
|
||||||
|
'X': true,
|
||||||
|
'Y': true,
|
||||||
|
'Z': true,
|
||||||
|
'^': true,
|
||||||
|
'_': true,
|
||||||
|
'`': true,
|
||||||
|
'a': true,
|
||||||
|
'b': true,
|
||||||
|
'c': true,
|
||||||
|
'd': true,
|
||||||
|
'e': true,
|
||||||
|
'f': true,
|
||||||
|
'g': true,
|
||||||
|
'h': true,
|
||||||
|
'i': true,
|
||||||
|
'j': true,
|
||||||
|
'k': true,
|
||||||
|
'l': true,
|
||||||
|
'm': true,
|
||||||
|
'n': true,
|
||||||
|
'o': true,
|
||||||
|
'p': true,
|
||||||
|
'q': true,
|
||||||
|
'r': true,
|
||||||
|
's': true,
|
||||||
|
't': true,
|
||||||
|
'u': true,
|
||||||
|
'v': true,
|
||||||
|
'w': true,
|
||||||
|
'x': true,
|
||||||
|
'y': true,
|
||||||
|
'z': true,
|
||||||
|
'|': true,
|
||||||
|
'~': true,
|
||||||
|
}
|
||||||
|
|
||||||
|
type connectionStater interface {
|
||||||
|
ConnectionState() tls.ConnectionState
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !go1.5
|
||||||
|
|
||||||
|
package http2
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
func requestCancel(req *http.Request) <-chan struct{} { return nil }
|
|
@ -0,0 +1,13 @@
|
||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !go1.6
|
||||||
|
|
||||||
|
package http2
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
func configureTransport(t1 *http.Transport) (*Transport, error) {
|
||||||
|
return nil, errTransportVersion
|
||||||
|
}
|
|
@ -1,43 +1,147 @@
|
||||||
// Copyright 2014 The Go Authors.
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
|
// Use of this source code is governed by a BSD-style
|
||||||
// Licensed under the same terms as Go itself:
|
// license that can be found in the LICENSE file.
|
||||||
// https://code.google.com/p/go/source/browse/LICENSE
|
|
||||||
|
|
||||||
package http2
|
package http2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// pipe is a goroutine-safe io.Reader/io.Writer pair. It's like
|
||||||
|
// io.Pipe except there are no PipeReader/PipeWriter halves, and the
|
||||||
|
// underlying buffer is an interface. (io.Pipe is always unbuffered)
|
||||||
type pipe struct {
|
type pipe struct {
|
||||||
b buffer
|
mu sync.Mutex
|
||||||
c sync.Cond
|
c sync.Cond // c.L lazily initialized to &p.mu
|
||||||
m sync.Mutex
|
b pipeBuffer
|
||||||
|
err error // read error once empty. non-nil means closed.
|
||||||
|
breakErr error // immediate read error (caller doesn't see rest of b)
|
||||||
|
donec chan struct{} // closed on error
|
||||||
|
readFn func() // optional code to run in Read before error
|
||||||
|
}
|
||||||
|
|
||||||
|
type pipeBuffer interface {
|
||||||
|
Len() int
|
||||||
|
io.Writer
|
||||||
|
io.Reader
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read waits until data is available and copies bytes
|
// Read waits until data is available and copies bytes
|
||||||
// from the buffer into p.
|
// from the buffer into p.
|
||||||
func (r *pipe) Read(p []byte) (n int, err error) {
|
func (p *pipe) Read(d []byte) (n int, err error) {
|
||||||
r.c.L.Lock()
|
p.mu.Lock()
|
||||||
defer r.c.L.Unlock()
|
defer p.mu.Unlock()
|
||||||
for r.b.Len() == 0 && !r.b.closed {
|
if p.c.L == nil {
|
||||||
r.c.Wait()
|
p.c.L = &p.mu
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
if p.breakErr != nil {
|
||||||
|
return 0, p.breakErr
|
||||||
|
}
|
||||||
|
if p.b.Len() > 0 {
|
||||||
|
return p.b.Read(d)
|
||||||
|
}
|
||||||
|
if p.err != nil {
|
||||||
|
if p.readFn != nil {
|
||||||
|
p.readFn() // e.g. copy trailers
|
||||||
|
p.readFn = nil // not sticky like p.err
|
||||||
|
}
|
||||||
|
return 0, p.err
|
||||||
|
}
|
||||||
|
p.c.Wait()
|
||||||
}
|
}
|
||||||
return r.b.Read(p)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var errClosedPipeWrite = errors.New("write on closed buffer")
|
||||||
|
|
||||||
// Write copies bytes from p into the buffer and wakes a reader.
|
// Write copies bytes from p into the buffer and wakes a reader.
|
||||||
// It is an error to write more data than the buffer can hold.
|
// It is an error to write more data than the buffer can hold.
|
||||||
func (w *pipe) Write(p []byte) (n int, err error) {
|
func (p *pipe) Write(d []byte) (n int, err error) {
|
||||||
w.c.L.Lock()
|
p.mu.Lock()
|
||||||
defer w.c.L.Unlock()
|
defer p.mu.Unlock()
|
||||||
defer w.c.Signal()
|
if p.c.L == nil {
|
||||||
return w.b.Write(p)
|
p.c.L = &p.mu
|
||||||
|
}
|
||||||
|
defer p.c.Signal()
|
||||||
|
if p.err != nil {
|
||||||
|
return 0, errClosedPipeWrite
|
||||||
|
}
|
||||||
|
return p.b.Write(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *pipe) Close(err error) {
|
// CloseWithError causes the next Read (waking up a current blocked
|
||||||
c.c.L.Lock()
|
// Read if needed) to return the provided err after all data has been
|
||||||
defer c.c.L.Unlock()
|
// read.
|
||||||
defer c.c.Signal()
|
//
|
||||||
c.b.Close(err)
|
// The error must be non-nil.
|
||||||
|
func (p *pipe) CloseWithError(err error) { p.closeWithError(&p.err, err, nil) }
|
||||||
|
|
||||||
|
// BreakWithError causes the next Read (waking up a current blocked
|
||||||
|
// Read if needed) to return the provided err immediately, without
|
||||||
|
// waiting for unread data.
|
||||||
|
func (p *pipe) BreakWithError(err error) { p.closeWithError(&p.breakErr, err, nil) }
|
||||||
|
|
||||||
|
// closeWithErrorAndCode is like CloseWithError but also sets some code to run
|
||||||
|
// in the caller's goroutine before returning the error.
|
||||||
|
func (p *pipe) closeWithErrorAndCode(err error, fn func()) { p.closeWithError(&p.err, err, fn) }
|
||||||
|
|
||||||
|
func (p *pipe) closeWithError(dst *error, err error, fn func()) {
|
||||||
|
if err == nil {
|
||||||
|
panic("err must be non-nil")
|
||||||
|
}
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
if p.c.L == nil {
|
||||||
|
p.c.L = &p.mu
|
||||||
|
}
|
||||||
|
defer p.c.Signal()
|
||||||
|
if *dst != nil {
|
||||||
|
// Already been done.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.readFn = fn
|
||||||
|
*dst = err
|
||||||
|
p.closeDoneLocked()
|
||||||
|
}
|
||||||
|
|
||||||
|
// requires p.mu be held.
|
||||||
|
func (p *pipe) closeDoneLocked() {
|
||||||
|
if p.donec == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Close if unclosed. This isn't racy since we always
|
||||||
|
// hold p.mu while closing.
|
||||||
|
select {
|
||||||
|
case <-p.donec:
|
||||||
|
default:
|
||||||
|
close(p.donec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err returns the error (if any) first set by BreakWithError or CloseWithError.
|
||||||
|
func (p *pipe) Err() error {
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
if p.breakErr != nil {
|
||||||
|
return p.breakErr
|
||||||
|
}
|
||||||
|
return p.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done returns a channel which is closed if and when this pipe is closed
|
||||||
|
// with CloseWithError.
|
||||||
|
func (p *pipe) Done() <-chan struct{} {
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
if p.donec == nil {
|
||||||
|
p.donec = make(chan struct{})
|
||||||
|
if p.err != nil || p.breakErr != nil {
|
||||||
|
// Already hit an error.
|
||||||
|
p.closeDoneLocked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p.donec
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,16 +1,15 @@
|
||||||
// Copyright 2014 The Go Authors. All rights reserved.
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
|
|
||||||
// Licensed under the same terms as Go itself:
|
|
||||||
// https://code.google.com/p/go/source/browse/LICENSE
|
|
||||||
|
|
||||||
package http2
|
package http2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/net/http2/hpack"
|
"golang.org/x/net/http2/hpack"
|
||||||
|
@ -26,7 +25,11 @@ type writeFramer interface {
|
||||||
// frame writing scheduler (see writeScheduler in writesched.go).
|
// frame writing scheduler (see writeScheduler in writesched.go).
|
||||||
//
|
//
|
||||||
// This interface is implemented by *serverConn.
|
// This interface is implemented by *serverConn.
|
||||||
// TODO: use it from the client code too, once it exists.
|
//
|
||||||
|
// TODO: decide whether to a) use this in the client code (which didn't
|
||||||
|
// end up using this yet, because it has a simpler design, not
|
||||||
|
// currently implementing priorities), or b) delete this and
|
||||||
|
// make the server code a bit more concrete.
|
||||||
type writeContext interface {
|
type writeContext interface {
|
||||||
Framer() *Framer
|
Framer() *Framer
|
||||||
Flush() error
|
Flush() error
|
||||||
|
@ -44,6 +47,11 @@ func endsStream(w writeFramer) bool {
|
||||||
return v.endStream
|
return v.endStream
|
||||||
case *writeResHeaders:
|
case *writeResHeaders:
|
||||||
return v.endStream
|
return v.endStream
|
||||||
|
case nil:
|
||||||
|
// This can only happen if the caller reuses w after it's
|
||||||
|
// been intentionally nil'ed out to prevent use. Keep this
|
||||||
|
// here to catch future refactoring breaking it.
|
||||||
|
panic("endsStream called on nil writeFramer")
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -89,6 +97,16 @@ func (w *writeData) writeFrame(ctx writeContext) error {
|
||||||
return ctx.Framer().WriteData(w.streamID, w.endStream, w.p)
|
return ctx.Framer().WriteData(w.streamID, w.endStream, w.p)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handlerPanicRST is the message sent from handler goroutines when
|
||||||
|
// the handler panics.
|
||||||
|
type handlerPanicRST struct {
|
||||||
|
StreamID uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hp handlerPanicRST) writeFrame(ctx writeContext) error {
|
||||||
|
return ctx.Framer().WriteRSTStream(hp.StreamID, ErrCodeInternal)
|
||||||
|
}
|
||||||
|
|
||||||
func (se StreamError) writeFrame(ctx writeContext) error {
|
func (se StreamError) writeFrame(ctx writeContext) error {
|
||||||
return ctx.Framer().WriteRSTStream(se.StreamID, se.Code)
|
return ctx.Framer().WriteRSTStream(se.StreamID, se.Code)
|
||||||
}
|
}
|
||||||
|
@ -106,40 +124,48 @@ func (writeSettingsAck) writeFrame(ctx writeContext) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeResHeaders is a request to write a HEADERS and 0+ CONTINUATION frames
|
// writeResHeaders is a request to write a HEADERS and 0+ CONTINUATION frames
|
||||||
// for HTTP response headers from a server handler.
|
// for HTTP response headers or trailers from a server handler.
|
||||||
type writeResHeaders struct {
|
type writeResHeaders struct {
|
||||||
streamID uint32
|
streamID uint32
|
||||||
httpResCode int
|
httpResCode int // 0 means no ":status" line
|
||||||
h http.Header // may be nil
|
h http.Header // may be nil
|
||||||
|
trailers []string // if non-nil, which keys of h to write. nil means all.
|
||||||
endStream bool
|
endStream bool
|
||||||
|
|
||||||
|
date string
|
||||||
contentType string
|
contentType string
|
||||||
contentLength string
|
contentLength string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func encKV(enc *hpack.Encoder, k, v string) {
|
||||||
|
if VerboseLogs {
|
||||||
|
log.Printf("http2: server encoding header %q = %q", k, v)
|
||||||
|
}
|
||||||
|
enc.WriteField(hpack.HeaderField{Name: k, Value: v})
|
||||||
|
}
|
||||||
|
|
||||||
func (w *writeResHeaders) writeFrame(ctx writeContext) error {
|
func (w *writeResHeaders) writeFrame(ctx writeContext) error {
|
||||||
enc, buf := ctx.HeaderEncoder()
|
enc, buf := ctx.HeaderEncoder()
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
enc.WriteField(hpack.HeaderField{Name: ":status", Value: httpCodeString(w.httpResCode)})
|
|
||||||
for k, vv := range w.h {
|
if w.httpResCode != 0 {
|
||||||
k = lowerHeader(k)
|
encKV(enc, ":status", httpCodeString(w.httpResCode))
|
||||||
for _, v := range vv {
|
|
||||||
// TODO: more of "8.1.2.2 Connection-Specific Header Fields"
|
|
||||||
if k == "transfer-encoding" && v != "trailers" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
enc.WriteField(hpack.HeaderField{Name: k, Value: v})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
encodeHeaders(enc, w.h, w.trailers)
|
||||||
|
|
||||||
if w.contentType != "" {
|
if w.contentType != "" {
|
||||||
enc.WriteField(hpack.HeaderField{Name: "content-type", Value: w.contentType})
|
encKV(enc, "content-type", w.contentType)
|
||||||
}
|
}
|
||||||
if w.contentLength != "" {
|
if w.contentLength != "" {
|
||||||
enc.WriteField(hpack.HeaderField{Name: "content-length", Value: w.contentLength})
|
encKV(enc, "content-length", w.contentLength)
|
||||||
|
}
|
||||||
|
if w.date != "" {
|
||||||
|
encKV(enc, "date", w.date)
|
||||||
}
|
}
|
||||||
|
|
||||||
headerBlock := buf.Bytes()
|
headerBlock := buf.Bytes()
|
||||||
if len(headerBlock) == 0 {
|
if len(headerBlock) == 0 && w.trailers == nil {
|
||||||
panic("unexpected empty hpack")
|
panic("unexpected empty hpack")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,7 +211,7 @@ type write100ContinueHeadersFrame struct {
|
||||||
func (w write100ContinueHeadersFrame) writeFrame(ctx writeContext) error {
|
func (w write100ContinueHeadersFrame) writeFrame(ctx writeContext) error {
|
||||||
enc, buf := ctx.HeaderEncoder()
|
enc, buf := ctx.HeaderEncoder()
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
enc.WriteField(hpack.HeaderField{Name: ":status", Value: "100"})
|
encKV(enc, ":status", "100")
|
||||||
return ctx.Framer().WriteHeaders(HeadersFrameParam{
|
return ctx.Framer().WriteHeaders(HeadersFrameParam{
|
||||||
StreamID: w.streamID,
|
StreamID: w.streamID,
|
||||||
BlockFragment: buf.Bytes(),
|
BlockFragment: buf.Bytes(),
|
||||||
|
@ -202,3 +228,36 @@ type writeWindowUpdate struct {
|
||||||
func (wu writeWindowUpdate) writeFrame(ctx writeContext) error {
|
func (wu writeWindowUpdate) writeFrame(ctx writeContext) error {
|
||||||
return ctx.Framer().WriteWindowUpdate(wu.streamID, wu.n)
|
return ctx.Framer().WriteWindowUpdate(wu.streamID, wu.n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func encodeHeaders(enc *hpack.Encoder, h http.Header, keys []string) {
|
||||||
|
// TODO: garbage. pool sorters like http1? hot path for 1 key?
|
||||||
|
if keys == nil {
|
||||||
|
keys = make([]string, 0, len(h))
|
||||||
|
for k := range h {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
}
|
||||||
|
for _, k := range keys {
|
||||||
|
vv := h[k]
|
||||||
|
k = lowerHeader(k)
|
||||||
|
if !validHeaderFieldName(k) {
|
||||||
|
// TODO: return an error? golang.org/issue/14048
|
||||||
|
// For now just omit it.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
isTE := k == "transfer-encoding"
|
||||||
|
for _, v := range vv {
|
||||||
|
if !validHeaderFieldValue(v) {
|
||||||
|
// TODO: return an error? golang.org/issue/14048
|
||||||
|
// For now just omit it.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// TODO: more of "8.1.2.2 Connection-Specific Header Fields"
|
||||||
|
if isTE && v != "trailers" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
encKV(enc, k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
// Copyright 2014 The Go Authors. All rights reserved.
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
|
|
||||||
// Licensed under the same terms as Go itself:
|
|
||||||
// https://code.google.com/p/go/source/browse/LICENSE
|
|
||||||
|
|
||||||
package http2
|
package http2
|
||||||
|
|
||||||
|
|
|
@ -113,6 +113,7 @@ func init() {
|
||||||
http.Error(w, "not allowed", http.StatusUnauthorized)
|
http.Error(w, "not allowed", http.StatusUnauthorized)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
Render(w, req, sensitive)
|
Render(w, req, sensitive)
|
||||||
})
|
})
|
||||||
http.HandleFunc("/debug/events", func(w http.ResponseWriter, req *http.Request) {
|
http.HandleFunc("/debug/events", func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
@ -121,6 +122,7 @@ func init() {
|
||||||
http.Error(w, "not allowed", http.StatusUnauthorized)
|
http.Error(w, "not allowed", http.StatusUnauthorized)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
RenderEvents(w, req, sensitive)
|
RenderEvents(w, req, sensitive)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -172,7 +174,7 @@ func Render(w io.Writer, req *http.Request, sensitive bool) {
|
||||||
|
|
||||||
completedMu.RLock()
|
completedMu.RLock()
|
||||||
data.Families = make([]string, 0, len(completedTraces))
|
data.Families = make([]string, 0, len(completedTraces))
|
||||||
for fam, _ := range completedTraces {
|
for fam := range completedTraces {
|
||||||
data.Families = append(data.Families, fam)
|
data.Families = append(data.Families, fam)
|
||||||
}
|
}
|
||||||
completedMu.RUnlock()
|
completedMu.RUnlock()
|
||||||
|
|
Loading…
Reference in New Issue