Update ingress godeps
This commit is contained in:
parent
d43021b3f1
commit
28db8fb16d
1068 changed files with 461467 additions and 117300 deletions
295
vendor/golang.org/x/net/context/context.go
generated
vendored
295
vendor/golang.org/x/net/context/context.go
generated
vendored
|
|
@ -36,12 +36,7 @@
|
|||
// Contexts.
|
||||
package context
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
import "time"
|
||||
|
||||
// A Context carries a deadline, a cancelation signal, and other values across
|
||||
// API boundaries.
|
||||
|
|
@ -66,7 +61,7 @@ type Context interface {
|
|||
//
|
||||
// // Stream generates values with DoSomething and sends them to out
|
||||
// // until DoSomething returns an error or ctx.Done is closed.
|
||||
// func Stream(ctx context.Context, out <-chan Value) error {
|
||||
// func Stream(ctx context.Context, out chan<- Value) error {
|
||||
// for {
|
||||
// v, err := DoSomething(ctx)
|
||||
// if err != nil {
|
||||
|
|
@ -138,48 +133,6 @@ type Context interface {
|
|||
Value(key interface{}) interface{}
|
||||
}
|
||||
|
||||
// Canceled is the error returned by Context.Err when the context is canceled.
|
||||
var Canceled = errors.New("context canceled")
|
||||
|
||||
// DeadlineExceeded is the error returned by Context.Err when the context's
|
||||
// deadline passes.
|
||||
var DeadlineExceeded = errors.New("context deadline exceeded")
|
||||
|
||||
// An emptyCtx is never canceled, has no values, and has no deadline. It is not
|
||||
// struct{}, since vars of this type must have distinct addresses.
|
||||
type emptyCtx int
|
||||
|
||||
func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (*emptyCtx) Done() <-chan struct{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*emptyCtx) Err() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*emptyCtx) Value(key interface{}) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *emptyCtx) String() string {
|
||||
switch e {
|
||||
case background:
|
||||
return "context.Background"
|
||||
case todo:
|
||||
return "context.TODO"
|
||||
}
|
||||
return "unknown empty Context"
|
||||
}
|
||||
|
||||
var (
|
||||
background = new(emptyCtx)
|
||||
todo = new(emptyCtx)
|
||||
)
|
||||
|
||||
// Background returns a non-nil, empty Context. It is never canceled, has no
|
||||
// values, and has no deadline. It is typically used by the main function,
|
||||
// initialization, and tests, and as the top-level Context for incoming
|
||||
|
|
@ -201,247 +154,3 @@ func TODO() Context {
|
|||
// A CancelFunc does not wait for the work to stop.
|
||||
// After the first call, subsequent calls to a CancelFunc do nothing.
|
||||
type CancelFunc func()
|
||||
|
||||
// WithCancel returns a copy of parent with a new Done channel. The returned
|
||||
// context's Done channel is closed when the returned cancel function is called
|
||||
// or when the parent context's Done channel is closed, whichever happens first.
|
||||
//
|
||||
// Canceling this context releases resources associated with it, so code should
|
||||
// call cancel as soon as the operations running in this Context complete.
|
||||
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
|
||||
c := newCancelCtx(parent)
|
||||
propagateCancel(parent, &c)
|
||||
return &c, func() { c.cancel(true, Canceled) }
|
||||
}
|
||||
|
||||
// newCancelCtx returns an initialized cancelCtx.
|
||||
func newCancelCtx(parent Context) cancelCtx {
|
||||
return cancelCtx{
|
||||
Context: parent,
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// propagateCancel arranges for child to be canceled when parent is.
|
||||
func propagateCancel(parent Context, child canceler) {
|
||||
if parent.Done() == nil {
|
||||
return // parent is never canceled
|
||||
}
|
||||
if p, ok := parentCancelCtx(parent); ok {
|
||||
p.mu.Lock()
|
||||
if p.err != nil {
|
||||
// parent has already been canceled
|
||||
child.cancel(false, p.err)
|
||||
} else {
|
||||
if p.children == nil {
|
||||
p.children = make(map[canceler]bool)
|
||||
}
|
||||
p.children[child] = true
|
||||
}
|
||||
p.mu.Unlock()
|
||||
} else {
|
||||
go func() {
|
||||
select {
|
||||
case <-parent.Done():
|
||||
child.cancel(false, parent.Err())
|
||||
case <-child.Done():
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
// parentCancelCtx follows a chain of parent references until it finds a
|
||||
// *cancelCtx. This function understands how each of the concrete types in this
|
||||
// package represents its parent.
|
||||
func parentCancelCtx(parent Context) (*cancelCtx, bool) {
|
||||
for {
|
||||
switch c := parent.(type) {
|
||||
case *cancelCtx:
|
||||
return c, true
|
||||
case *timerCtx:
|
||||
return &c.cancelCtx, true
|
||||
case *valueCtx:
|
||||
parent = c.Context
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// removeChild removes a context from its parent.
|
||||
func removeChild(parent Context, child canceler) {
|
||||
p, ok := parentCancelCtx(parent)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
p.mu.Lock()
|
||||
if p.children != nil {
|
||||
delete(p.children, child)
|
||||
}
|
||||
p.mu.Unlock()
|
||||
}
|
||||
|
||||
// A canceler is a context type that can be canceled directly. The
|
||||
// implementations are *cancelCtx and *timerCtx.
|
||||
type canceler interface {
|
||||
cancel(removeFromParent bool, err error)
|
||||
Done() <-chan struct{}
|
||||
}
|
||||
|
||||
// A cancelCtx can be canceled. When canceled, it also cancels any children
|
||||
// that implement canceler.
|
||||
type cancelCtx struct {
|
||||
Context
|
||||
|
||||
done chan struct{} // closed by the first cancel call.
|
||||
|
||||
mu sync.Mutex
|
||||
children map[canceler]bool // set to nil by the first cancel call
|
||||
err error // set to non-nil by the first cancel call
|
||||
}
|
||||
|
||||
func (c *cancelCtx) Done() <-chan struct{} {
|
||||
return c.done
|
||||
}
|
||||
|
||||
func (c *cancelCtx) Err() error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
return c.err
|
||||
}
|
||||
|
||||
func (c *cancelCtx) String() string {
|
||||
return fmt.Sprintf("%v.WithCancel", c.Context)
|
||||
}
|
||||
|
||||
// cancel closes c.done, cancels each of c's children, and, if
|
||||
// removeFromParent is true, removes c from its parent's children.
|
||||
func (c *cancelCtx) cancel(removeFromParent bool, err error) {
|
||||
if err == nil {
|
||||
panic("context: internal error: missing cancel error")
|
||||
}
|
||||
c.mu.Lock()
|
||||
if c.err != nil {
|
||||
c.mu.Unlock()
|
||||
return // already canceled
|
||||
}
|
||||
c.err = err
|
||||
close(c.done)
|
||||
for child := range c.children {
|
||||
// NOTE: acquiring the child's lock while holding parent's lock.
|
||||
child.cancel(false, err)
|
||||
}
|
||||
c.children = nil
|
||||
c.mu.Unlock()
|
||||
|
||||
if removeFromParent {
|
||||
removeChild(c.Context, c)
|
||||
}
|
||||
}
|
||||
|
||||
// WithDeadline returns a copy of the parent context with the deadline adjusted
|
||||
// to be no later than d. If the parent's deadline is already earlier than d,
|
||||
// WithDeadline(parent, d) is semantically equivalent to parent. The returned
|
||||
// context's Done channel is closed when the deadline expires, when the returned
|
||||
// cancel function is called, or when the parent context's Done channel is
|
||||
// closed, whichever happens first.
|
||||
//
|
||||
// Canceling this context releases resources associated with it, so code should
|
||||
// call cancel as soon as the operations running in this Context complete.
|
||||
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
|
||||
if cur, ok := parent.Deadline(); ok && cur.Before(deadline) {
|
||||
// The current deadline is already sooner than the new one.
|
||||
return WithCancel(parent)
|
||||
}
|
||||
c := &timerCtx{
|
||||
cancelCtx: newCancelCtx(parent),
|
||||
deadline: deadline,
|
||||
}
|
||||
propagateCancel(parent, c)
|
||||
d := deadline.Sub(time.Now())
|
||||
if d <= 0 {
|
||||
c.cancel(true, DeadlineExceeded) // deadline has already passed
|
||||
return c, func() { c.cancel(true, Canceled) }
|
||||
}
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if c.err == nil {
|
||||
c.timer = time.AfterFunc(d, func() {
|
||||
c.cancel(true, DeadlineExceeded)
|
||||
})
|
||||
}
|
||||
return c, func() { c.cancel(true, Canceled) }
|
||||
}
|
||||
|
||||
// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to
|
||||
// implement Done and Err. It implements cancel by stopping its timer then
|
||||
// delegating to cancelCtx.cancel.
|
||||
type timerCtx struct {
|
||||
cancelCtx
|
||||
timer *time.Timer // Under cancelCtx.mu.
|
||||
|
||||
deadline time.Time
|
||||
}
|
||||
|
||||
func (c *timerCtx) Deadline() (deadline time.Time, ok bool) {
|
||||
return c.deadline, true
|
||||
}
|
||||
|
||||
func (c *timerCtx) String() string {
|
||||
return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, c.deadline.Sub(time.Now()))
|
||||
}
|
||||
|
||||
func (c *timerCtx) cancel(removeFromParent bool, err error) {
|
||||
c.cancelCtx.cancel(false, err)
|
||||
if removeFromParent {
|
||||
// Remove this timerCtx from its parent cancelCtx's children.
|
||||
removeChild(c.cancelCtx.Context, c)
|
||||
}
|
||||
c.mu.Lock()
|
||||
if c.timer != nil {
|
||||
c.timer.Stop()
|
||||
c.timer = nil
|
||||
}
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
|
||||
//
|
||||
// Canceling this context releases resources associated with it, so code should
|
||||
// call cancel as soon as the operations running in this Context complete:
|
||||
//
|
||||
// func slowOperationWithTimeout(ctx context.Context) (Result, error) {
|
||||
// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
|
||||
// defer cancel() // releases resources if slowOperation completes before timeout elapses
|
||||
// return slowOperation(ctx)
|
||||
// }
|
||||
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
|
||||
return WithDeadline(parent, time.Now().Add(timeout))
|
||||
}
|
||||
|
||||
// WithValue returns a copy of parent in which the value associated with key is
|
||||
// val.
|
||||
//
|
||||
// Use context Values only for request-scoped data that transits processes and
|
||||
// APIs, not for passing optional parameters to functions.
|
||||
func WithValue(parent Context, key interface{}, val interface{}) Context {
|
||||
return &valueCtx{parent, key, val}
|
||||
}
|
||||
|
||||
// A valueCtx carries a key-value pair. It implements Value for that key and
|
||||
// delegates all other calls to the embedded Context.
|
||||
type valueCtx struct {
|
||||
Context
|
||||
key, val interface{}
|
||||
}
|
||||
|
||||
func (c *valueCtx) String() string {
|
||||
return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val)
|
||||
}
|
||||
|
||||
func (c *valueCtx) Value(key interface{}) interface{} {
|
||||
if c.key == key {
|
||||
return c.val
|
||||
}
|
||||
return c.Context.Value(key)
|
||||
}
|
||||
|
|
|
|||
19
vendor/golang.org/x/net/context/ctxhttp/cancelreq.go
generated
vendored
19
vendor/golang.org/x/net/context/ctxhttp/cancelreq.go
generated
vendored
|
|
@ -1,19 +0,0 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.5
|
||||
|
||||
package ctxhttp
|
||||
|
||||
import "net/http"
|
||||
|
||||
func canceler(client *http.Client, req *http.Request) func() {
|
||||
// TODO(djd): Respect any existing value of req.Cancel.
|
||||
ch := make(chan struct{})
|
||||
req.Cancel = ch
|
||||
|
||||
return func() {
|
||||
close(ch)
|
||||
}
|
||||
}
|
||||
23
vendor/golang.org/x/net/context/ctxhttp/cancelreq_go14.go
generated
vendored
23
vendor/golang.org/x/net/context/ctxhttp/cancelreq_go14.go
generated
vendored
|
|
@ -1,23 +0,0 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !go1.5
|
||||
|
||||
package ctxhttp
|
||||
|
||||
import "net/http"
|
||||
|
||||
type requestCanceler interface {
|
||||
CancelRequest(*http.Request)
|
||||
}
|
||||
|
||||
func canceler(client *http.Client, req *http.Request) func() {
|
||||
rc, ok := client.Transport.(requestCanceler)
|
||||
if !ok {
|
||||
return func() {}
|
||||
}
|
||||
return func() {
|
||||
rc.CancelRequest(req)
|
||||
}
|
||||
}
|
||||
96
vendor/golang.org/x/net/context/ctxhttp/ctxhttp.go
generated
vendored
96
vendor/golang.org/x/net/context/ctxhttp/ctxhttp.go
generated
vendored
|
|
@ -1,7 +1,9 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.7
|
||||
|
||||
// Package ctxhttp provides helper functions for performing context-aware HTTP requests.
|
||||
package ctxhttp
|
||||
|
||||
|
|
@ -14,71 +16,18 @@ import (
|
|||
"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 context is canceled or times out, ctx.Err() will be returned.
|
||||
//
|
||||
// The provided ctx must be non-nil. If it is canceled or times out,
|
||||
// ctx.Err() will be returned.
|
||||
func Do(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) {
|
||||
if client == nil {
|
||||
client = http.DefaultClient
|
||||
}
|
||||
|
||||
// Request cancelation changed in Go 1.5, see cancelreq.go and cancelreq_go14.go.
|
||||
cancel := canceler(client, req)
|
||||
|
||||
type responseAndError struct {
|
||||
resp *http.Response
|
||||
err error
|
||||
}
|
||||
result := make(chan responseAndError, 1)
|
||||
|
||||
go func() {
|
||||
resp, err := client.Do(req)
|
||||
testHookDoReturned()
|
||||
result <- responseAndError{resp, err}
|
||||
}()
|
||||
|
||||
var resp *http.Response
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
testHookContextDoneBeforeHeaders()
|
||||
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()
|
||||
case r := <-result:
|
||||
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
|
||||
return client.Do(req.WithContext(ctx))
|
||||
}
|
||||
|
||||
// Get issues a GET request via the Do function.
|
||||
|
|
@ -113,28 +62,3 @@ 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) {
|
||||
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
|
||||
}
|
||||
|
|
|
|||
147
vendor/golang.org/x/net/context/ctxhttp/ctxhttp_pre17.go
generated
vendored
Normal file
147
vendor/golang.org/x/net/context/ctxhttp/ctxhttp_pre17.go
generated
vendored
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
// 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.7
|
||||
|
||||
package ctxhttp
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"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.
|
||||
// If the client is nil, http.DefaultClient is used.
|
||||
// If the context is canceled or times out, ctx.Err() will be returned.
|
||||
func Do(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) {
|
||||
if client == nil {
|
||||
client = http.DefaultClient
|
||||
}
|
||||
|
||||
// TODO(djd): Respect any existing value of req.Cancel.
|
||||
cancel := make(chan struct{})
|
||||
req.Cancel = cancel
|
||||
|
||||
type responseAndError struct {
|
||||
resp *http.Response
|
||||
err error
|
||||
}
|
||||
result := make(chan responseAndError, 1)
|
||||
|
||||
// Make local copies of test hooks closed over by goroutines below.
|
||||
// Prevents data races in tests.
|
||||
testHookDoReturned := testHookDoReturned
|
||||
testHookDidBodyClose := testHookDidBodyClose
|
||||
|
||||
go func() {
|
||||
resp, err := client.Do(req)
|
||||
testHookDoReturned()
|
||||
result <- responseAndError{resp, err}
|
||||
}()
|
||||
|
||||
var resp *http.Response
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
testHookContextDoneBeforeHeaders()
|
||||
close(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()
|
||||
case r := <-result:
|
||||
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():
|
||||
close(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.
|
||||
func Get(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Do(ctx, client, req)
|
||||
}
|
||||
|
||||
// Head issues a HEAD request via the Do function.
|
||||
func Head(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
|
||||
req, err := http.NewRequest("HEAD", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Do(ctx, client, req)
|
||||
}
|
||||
|
||||
// Post issues a POST request via the Do function.
|
||||
func Post(ctx context.Context, client *http.Client, url string, bodyType string, body io.Reader) (*http.Response, error) {
|
||||
req, err := http.NewRequest("POST", url, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", bodyType)
|
||||
return Do(ctx, client, req)
|
||||
}
|
||||
|
||||
// PostForm issues a POST request via the Do function.
|
||||
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()))
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
72
vendor/golang.org/x/net/context/go17.go
generated
vendored
Normal file
72
vendor/golang.org/x/net/context/go17.go
generated
vendored
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.7
|
||||
|
||||
package context
|
||||
|
||||
import (
|
||||
"context" // standard library's context, as of Go 1.7
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
todo = context.TODO()
|
||||
background = context.Background()
|
||||
)
|
||||
|
||||
// Canceled is the error returned by Context.Err when the context is canceled.
|
||||
var Canceled = context.Canceled
|
||||
|
||||
// DeadlineExceeded is the error returned by Context.Err when the context's
|
||||
// deadline passes.
|
||||
var DeadlineExceeded = context.DeadlineExceeded
|
||||
|
||||
// WithCancel returns a copy of parent with a new Done channel. The returned
|
||||
// context's Done channel is closed when the returned cancel function is called
|
||||
// or when the parent context's Done channel is closed, whichever happens first.
|
||||
//
|
||||
// Canceling this context releases resources associated with it, so code should
|
||||
// call cancel as soon as the operations running in this Context complete.
|
||||
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
|
||||
ctx, f := context.WithCancel(parent)
|
||||
return ctx, CancelFunc(f)
|
||||
}
|
||||
|
||||
// WithDeadline returns a copy of the parent context with the deadline adjusted
|
||||
// to be no later than d. If the parent's deadline is already earlier than d,
|
||||
// WithDeadline(parent, d) is semantically equivalent to parent. The returned
|
||||
// context's Done channel is closed when the deadline expires, when the returned
|
||||
// cancel function is called, or when the parent context's Done channel is
|
||||
// closed, whichever happens first.
|
||||
//
|
||||
// Canceling this context releases resources associated with it, so code should
|
||||
// call cancel as soon as the operations running in this Context complete.
|
||||
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
|
||||
ctx, f := context.WithDeadline(parent, deadline)
|
||||
return ctx, CancelFunc(f)
|
||||
}
|
||||
|
||||
// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
|
||||
//
|
||||
// Canceling this context releases resources associated with it, so code should
|
||||
// call cancel as soon as the operations running in this Context complete:
|
||||
//
|
||||
// func slowOperationWithTimeout(ctx context.Context) (Result, error) {
|
||||
// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
|
||||
// defer cancel() // releases resources if slowOperation completes before timeout elapses
|
||||
// return slowOperation(ctx)
|
||||
// }
|
||||
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
|
||||
return WithDeadline(parent, time.Now().Add(timeout))
|
||||
}
|
||||
|
||||
// WithValue returns a copy of parent in which the value associated with key is
|
||||
// val.
|
||||
//
|
||||
// Use context Values only for request-scoped data that transits processes and
|
||||
// APIs, not for passing optional parameters to functions.
|
||||
func WithValue(parent Context, key interface{}, val interface{}) Context {
|
||||
return context.WithValue(parent, key, val)
|
||||
}
|
||||
300
vendor/golang.org/x/net/context/pre_go17.go
generated
vendored
Normal file
300
vendor/golang.org/x/net/context/pre_go17.go
generated
vendored
Normal file
|
|
@ -0,0 +1,300 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !go1.7
|
||||
|
||||
package context
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// An emptyCtx is never canceled, has no values, and has no deadline. It is not
|
||||
// struct{}, since vars of this type must have distinct addresses.
|
||||
type emptyCtx int
|
||||
|
||||
func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (*emptyCtx) Done() <-chan struct{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*emptyCtx) Err() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*emptyCtx) Value(key interface{}) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *emptyCtx) String() string {
|
||||
switch e {
|
||||
case background:
|
||||
return "context.Background"
|
||||
case todo:
|
||||
return "context.TODO"
|
||||
}
|
||||
return "unknown empty Context"
|
||||
}
|
||||
|
||||
var (
|
||||
background = new(emptyCtx)
|
||||
todo = new(emptyCtx)
|
||||
)
|
||||
|
||||
// Canceled is the error returned by Context.Err when the context is canceled.
|
||||
var Canceled = errors.New("context canceled")
|
||||
|
||||
// DeadlineExceeded is the error returned by Context.Err when the context's
|
||||
// deadline passes.
|
||||
var DeadlineExceeded = errors.New("context deadline exceeded")
|
||||
|
||||
// WithCancel returns a copy of parent with a new Done channel. The returned
|
||||
// context's Done channel is closed when the returned cancel function is called
|
||||
// or when the parent context's Done channel is closed, whichever happens first.
|
||||
//
|
||||
// Canceling this context releases resources associated with it, so code should
|
||||
// call cancel as soon as the operations running in this Context complete.
|
||||
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
|
||||
c := newCancelCtx(parent)
|
||||
propagateCancel(parent, c)
|
||||
return c, func() { c.cancel(true, Canceled) }
|
||||
}
|
||||
|
||||
// newCancelCtx returns an initialized cancelCtx.
|
||||
func newCancelCtx(parent Context) *cancelCtx {
|
||||
return &cancelCtx{
|
||||
Context: parent,
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// propagateCancel arranges for child to be canceled when parent is.
|
||||
func propagateCancel(parent Context, child canceler) {
|
||||
if parent.Done() == nil {
|
||||
return // parent is never canceled
|
||||
}
|
||||
if p, ok := parentCancelCtx(parent); ok {
|
||||
p.mu.Lock()
|
||||
if p.err != nil {
|
||||
// parent has already been canceled
|
||||
child.cancel(false, p.err)
|
||||
} else {
|
||||
if p.children == nil {
|
||||
p.children = make(map[canceler]bool)
|
||||
}
|
||||
p.children[child] = true
|
||||
}
|
||||
p.mu.Unlock()
|
||||
} else {
|
||||
go func() {
|
||||
select {
|
||||
case <-parent.Done():
|
||||
child.cancel(false, parent.Err())
|
||||
case <-child.Done():
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
// parentCancelCtx follows a chain of parent references until it finds a
|
||||
// *cancelCtx. This function understands how each of the concrete types in this
|
||||
// package represents its parent.
|
||||
func parentCancelCtx(parent Context) (*cancelCtx, bool) {
|
||||
for {
|
||||
switch c := parent.(type) {
|
||||
case *cancelCtx:
|
||||
return c, true
|
||||
case *timerCtx:
|
||||
return c.cancelCtx, true
|
||||
case *valueCtx:
|
||||
parent = c.Context
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// removeChild removes a context from its parent.
|
||||
func removeChild(parent Context, child canceler) {
|
||||
p, ok := parentCancelCtx(parent)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
p.mu.Lock()
|
||||
if p.children != nil {
|
||||
delete(p.children, child)
|
||||
}
|
||||
p.mu.Unlock()
|
||||
}
|
||||
|
||||
// A canceler is a context type that can be canceled directly. The
|
||||
// implementations are *cancelCtx and *timerCtx.
|
||||
type canceler interface {
|
||||
cancel(removeFromParent bool, err error)
|
||||
Done() <-chan struct{}
|
||||
}
|
||||
|
||||
// A cancelCtx can be canceled. When canceled, it also cancels any children
|
||||
// that implement canceler.
|
||||
type cancelCtx struct {
|
||||
Context
|
||||
|
||||
done chan struct{} // closed by the first cancel call.
|
||||
|
||||
mu sync.Mutex
|
||||
children map[canceler]bool // set to nil by the first cancel call
|
||||
err error // set to non-nil by the first cancel call
|
||||
}
|
||||
|
||||
func (c *cancelCtx) Done() <-chan struct{} {
|
||||
return c.done
|
||||
}
|
||||
|
||||
func (c *cancelCtx) Err() error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
return c.err
|
||||
}
|
||||
|
||||
func (c *cancelCtx) String() string {
|
||||
return fmt.Sprintf("%v.WithCancel", c.Context)
|
||||
}
|
||||
|
||||
// cancel closes c.done, cancels each of c's children, and, if
|
||||
// removeFromParent is true, removes c from its parent's children.
|
||||
func (c *cancelCtx) cancel(removeFromParent bool, err error) {
|
||||
if err == nil {
|
||||
panic("context: internal error: missing cancel error")
|
||||
}
|
||||
c.mu.Lock()
|
||||
if c.err != nil {
|
||||
c.mu.Unlock()
|
||||
return // already canceled
|
||||
}
|
||||
c.err = err
|
||||
close(c.done)
|
||||
for child := range c.children {
|
||||
// NOTE: acquiring the child's lock while holding parent's lock.
|
||||
child.cancel(false, err)
|
||||
}
|
||||
c.children = nil
|
||||
c.mu.Unlock()
|
||||
|
||||
if removeFromParent {
|
||||
removeChild(c.Context, c)
|
||||
}
|
||||
}
|
||||
|
||||
// WithDeadline returns a copy of the parent context with the deadline adjusted
|
||||
// to be no later than d. If the parent's deadline is already earlier than d,
|
||||
// WithDeadline(parent, d) is semantically equivalent to parent. The returned
|
||||
// context's Done channel is closed when the deadline expires, when the returned
|
||||
// cancel function is called, or when the parent context's Done channel is
|
||||
// closed, whichever happens first.
|
||||
//
|
||||
// Canceling this context releases resources associated with it, so code should
|
||||
// call cancel as soon as the operations running in this Context complete.
|
||||
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
|
||||
if cur, ok := parent.Deadline(); ok && cur.Before(deadline) {
|
||||
// The current deadline is already sooner than the new one.
|
||||
return WithCancel(parent)
|
||||
}
|
||||
c := &timerCtx{
|
||||
cancelCtx: newCancelCtx(parent),
|
||||
deadline: deadline,
|
||||
}
|
||||
propagateCancel(parent, c)
|
||||
d := deadline.Sub(time.Now())
|
||||
if d <= 0 {
|
||||
c.cancel(true, DeadlineExceeded) // deadline has already passed
|
||||
return c, func() { c.cancel(true, Canceled) }
|
||||
}
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if c.err == nil {
|
||||
c.timer = time.AfterFunc(d, func() {
|
||||
c.cancel(true, DeadlineExceeded)
|
||||
})
|
||||
}
|
||||
return c, func() { c.cancel(true, Canceled) }
|
||||
}
|
||||
|
||||
// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to
|
||||
// implement Done and Err. It implements cancel by stopping its timer then
|
||||
// delegating to cancelCtx.cancel.
|
||||
type timerCtx struct {
|
||||
*cancelCtx
|
||||
timer *time.Timer // Under cancelCtx.mu.
|
||||
|
||||
deadline time.Time
|
||||
}
|
||||
|
||||
func (c *timerCtx) Deadline() (deadline time.Time, ok bool) {
|
||||
return c.deadline, true
|
||||
}
|
||||
|
||||
func (c *timerCtx) String() string {
|
||||
return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, c.deadline.Sub(time.Now()))
|
||||
}
|
||||
|
||||
func (c *timerCtx) cancel(removeFromParent bool, err error) {
|
||||
c.cancelCtx.cancel(false, err)
|
||||
if removeFromParent {
|
||||
// Remove this timerCtx from its parent cancelCtx's children.
|
||||
removeChild(c.cancelCtx.Context, c)
|
||||
}
|
||||
c.mu.Lock()
|
||||
if c.timer != nil {
|
||||
c.timer.Stop()
|
||||
c.timer = nil
|
||||
}
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
|
||||
//
|
||||
// Canceling this context releases resources associated with it, so code should
|
||||
// call cancel as soon as the operations running in this Context complete:
|
||||
//
|
||||
// func slowOperationWithTimeout(ctx context.Context) (Result, error) {
|
||||
// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
|
||||
// defer cancel() // releases resources if slowOperation completes before timeout elapses
|
||||
// return slowOperation(ctx)
|
||||
// }
|
||||
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
|
||||
return WithDeadline(parent, time.Now().Add(timeout))
|
||||
}
|
||||
|
||||
// WithValue returns a copy of parent in which the value associated with key is
|
||||
// val.
|
||||
//
|
||||
// Use context Values only for request-scoped data that transits processes and
|
||||
// APIs, not for passing optional parameters to functions.
|
||||
func WithValue(parent Context, key interface{}, val interface{}) Context {
|
||||
return &valueCtx{parent, key, val}
|
||||
}
|
||||
|
||||
// A valueCtx carries a key-value pair. It implements Value for that key and
|
||||
// delegates all other calls to the embedded Context.
|
||||
type valueCtx struct {
|
||||
Context
|
||||
key, val interface{}
|
||||
}
|
||||
|
||||
func (c *valueCtx) String() string {
|
||||
return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val)
|
||||
}
|
||||
|
||||
func (c *valueCtx) Value(key interface{}) interface{} {
|
||||
if c.key == key {
|
||||
return c.val
|
||||
}
|
||||
return c.Context.Value(key)
|
||||
}
|
||||
32
vendor/golang.org/x/net/http2/client_conn_pool.go
generated
vendored
32
vendor/golang.org/x/net/http2/client_conn_pool.go
generated
vendored
|
|
@ -18,6 +18,18 @@ type ClientConnPool interface {
|
|||
MarkDead(*ClientConn)
|
||||
}
|
||||
|
||||
// clientConnPoolIdleCloser is the interface implemented by ClientConnPool
|
||||
// implementations which can close their idle connections.
|
||||
type clientConnPoolIdleCloser interface {
|
||||
ClientConnPool
|
||||
closeIdleConnections()
|
||||
}
|
||||
|
||||
var (
|
||||
_ clientConnPoolIdleCloser = (*clientConnPool)(nil)
|
||||
_ clientConnPoolIdleCloser = noDialClientConnPool{}
|
||||
)
|
||||
|
||||
// TODO: use singleflight for dialing and addConnCalls?
|
||||
type clientConnPool struct {
|
||||
t *Transport
|
||||
|
|
@ -40,7 +52,16 @@ const (
|
|||
noDialOnMiss = false
|
||||
)
|
||||
|
||||
func (p *clientConnPool) getClientConn(_ *http.Request, addr string, dialOnMiss bool) (*ClientConn, error) {
|
||||
func (p *clientConnPool) getClientConn(req *http.Request, addr string, dialOnMiss bool) (*ClientConn, error) {
|
||||
if req.Close && dialOnMiss {
|
||||
// It gets its own connection.
|
||||
cc, err := p.t.dialClientConn(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc.singleUse = true
|
||||
return cc, nil
|
||||
}
|
||||
p.mu.Lock()
|
||||
for _, cc := range p.conns[addr] {
|
||||
if cc.CanTakeNewRequest() {
|
||||
|
|
@ -223,3 +244,12 @@ func filterOutClientConn(in []*ClientConn, exclude *ClientConn) []*ClientConn {
|
|||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
|
|
|||
11
vendor/golang.org/x/net/http2/configure_transport.go
generated
vendored
11
vendor/golang.org/x/net/http2/configure_transport.go
generated
vendored
|
|
@ -32,7 +32,7 @@ func configureTransport(t1 *http.Transport) (*Transport, error) {
|
|||
t1.TLSClientConfig.NextProtos = append(t1.TLSClientConfig.NextProtos, "http/1.1")
|
||||
}
|
||||
upgradeFn := func(authority string, c *tls.Conn) http.RoundTripper {
|
||||
addr := authorityAddr(authority)
|
||||
addr := authorityAddr("https", authority)
|
||||
if used, err := connPool.addConnIfNeeded(addr, t2, c); err != nil {
|
||||
go c.Close()
|
||||
return erringRoundTripper{err}
|
||||
|
|
@ -67,15 +67,6 @@ func registerHTTPSProtocol(t *http.Transport, rt http.RoundTripper) (err error)
|
|||
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 }
|
||||
|
|
|
|||
34
vendor/golang.org/x/net/http2/errors.go
generated
vendored
34
vendor/golang.org/x/net/http2/errors.go
generated
vendored
|
|
@ -4,7 +4,10 @@
|
|||
|
||||
package http2
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// An ErrCode is an unsigned 32-bit error code as defined in the HTTP/2 spec.
|
||||
type ErrCode uint32
|
||||
|
|
@ -88,3 +91,32 @@ type connError struct {
|
|||
func (e connError) Error() string {
|
||||
return fmt.Sprintf("http2: connection error: %v: %v", e.Code, e.Reason)
|
||||
}
|
||||
|
||||
type pseudoHeaderError string
|
||||
|
||||
func (e pseudoHeaderError) Error() string {
|
||||
return fmt.Sprintf("invalid pseudo-header %q", string(e))
|
||||
}
|
||||
|
||||
type duplicatePseudoHeaderError string
|
||||
|
||||
func (e duplicatePseudoHeaderError) Error() string {
|
||||
return fmt.Sprintf("duplicate pseudo-header %q", string(e))
|
||||
}
|
||||
|
||||
type headerFieldNameError string
|
||||
|
||||
func (e headerFieldNameError) Error() string {
|
||||
return fmt.Sprintf("invalid header field name %q", string(e))
|
||||
}
|
||||
|
||||
type headerFieldValueError string
|
||||
|
||||
func (e headerFieldValueError) Error() string {
|
||||
return fmt.Sprintf("invalid header field value %q", string(e))
|
||||
}
|
||||
|
||||
var (
|
||||
errMixPseudoHeaderTypes = errors.New("mix of request and response pseudo headers")
|
||||
errPseudoAfterRegular = errors.New("pseudo header field after regular")
|
||||
)
|
||||
|
|
|
|||
250
vendor/golang.org/x/net/http2/frame.go
generated
vendored
250
vendor/golang.org/x/net/http2/frame.go
generated
vendored
|
|
@ -11,7 +11,11 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/net/http2/hpack"
|
||||
"golang.org/x/net/lex/httplex"
|
||||
)
|
||||
|
||||
const frameHeaderLen = 9
|
||||
|
|
@ -261,7 +265,7 @@ type Frame interface {
|
|||
type Framer struct {
|
||||
r io.Reader
|
||||
lastFrame Frame
|
||||
errReason string
|
||||
errDetail error
|
||||
|
||||
// lastHeaderStream is non-zero if the last frame was an
|
||||
// unfinished HEADERS/CONTINUATION.
|
||||
|
|
@ -293,8 +297,20 @@ type Framer struct {
|
|||
// 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.
|
||||
// It is not compatible with ReadMetaHeaders.
|
||||
AllowIllegalReads bool
|
||||
|
||||
// ReadMetaHeaders if non-nil causes ReadFrame to merge
|
||||
// HEADERS and CONTINUATION frames together and return
|
||||
// MetaHeadersFrame instead.
|
||||
ReadMetaHeaders *hpack.Decoder
|
||||
|
||||
// MaxHeaderListSize is the http2 MAX_HEADER_LIST_SIZE.
|
||||
// It's used only if ReadMetaHeaders is set; 0 means a sane default
|
||||
// (currently 16MB)
|
||||
// If the limit is hit, MetaHeadersFrame.Truncated is set true.
|
||||
MaxHeaderListSize uint32
|
||||
|
||||
// TODO: track which type of frame & with which flags was sent
|
||||
// last. Then return an error (unless AllowIllegalWrites) if
|
||||
// we're in the middle of a header block and a
|
||||
|
|
@ -307,6 +323,13 @@ type Framer struct {
|
|||
debugFramerBuf *bytes.Buffer
|
||||
}
|
||||
|
||||
func (fr *Framer) maxHeaderListSize() uint32 {
|
||||
if fr.MaxHeaderListSize == 0 {
|
||||
return 16 << 20 // sane default, per docs
|
||||
}
|
||||
return fr.MaxHeaderListSize
|
||||
}
|
||||
|
||||
func (f *Framer) startWrite(ftype FrameType, flags Flags, streamID uint32) {
|
||||
// Write the FrameHeader.
|
||||
f.wbuf = append(f.wbuf[:0],
|
||||
|
|
@ -402,6 +425,17 @@ func (fr *Framer) SetMaxReadFrameSize(v uint32) {
|
|||
fr.maxReadSize = v
|
||||
}
|
||||
|
||||
// ErrorDetail returns a more detailed error of the last error
|
||||
// returned by Framer.ReadFrame. For instance, if ReadFrame
|
||||
// returns a StreamError with code PROTOCOL_ERROR, ErrorDetail
|
||||
// will say exactly what was invalid. ErrorDetail is not guaranteed
|
||||
// to return a non-nil value and like the rest of the http2 package,
|
||||
// its return value is not protected by an API compatibility promise.
|
||||
// ErrorDetail is reset after the next call to ReadFrame.
|
||||
func (fr *Framer) ErrorDetail() error {
|
||||
return fr.errDetail
|
||||
}
|
||||
|
||||
// ErrFrameTooLarge is returned from Framer.ReadFrame when the peer
|
||||
// sends a frame that is larger than declared with SetMaxReadFrameSize.
|
||||
var ErrFrameTooLarge = errors.New("http2: frame too large")
|
||||
|
|
@ -420,9 +454,10 @@ func terminalReadFrameError(err error) bool {
|
|||
//
|
||||
// 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
|
||||
// ConnectionError, StreamError, or anything else from the underlying
|
||||
// reader.
|
||||
func (fr *Framer) ReadFrame() (Frame, error) {
|
||||
fr.errDetail = nil
|
||||
if fr.lastFrame != nil {
|
||||
fr.lastFrame.invalidate()
|
||||
}
|
||||
|
|
@ -450,6 +485,9 @@ func (fr *Framer) ReadFrame() (Frame, error) {
|
|||
if fr.logReads {
|
||||
log.Printf("http2: Framer %p: read %v", fr, summarizeFrame(f))
|
||||
}
|
||||
if fh.Type == FrameHeaders && fr.ReadMetaHeaders != nil {
|
||||
return fr.readMetaFrame(f.(*HeadersFrame))
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
|
|
@ -458,7 +496,7 @@ func (fr *Framer) ReadFrame() (Frame, error) {
|
|||
// 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
|
||||
fr.errDetail = errors.New(reason)
|
||||
return ConnectionError(code)
|
||||
}
|
||||
|
||||
|
|
@ -553,7 +591,14 @@ func parseDataFrame(fh FrameHeader, payload []byte) (Frame, error) {
|
|||
return f, nil
|
||||
}
|
||||
|
||||
var errStreamID = errors.New("invalid streamid")
|
||||
var (
|
||||
errStreamID = errors.New("invalid stream ID")
|
||||
errDepStreamID = errors.New("invalid dependent stream ID")
|
||||
)
|
||||
|
||||
func validStreamIDOrZero(streamID uint32) bool {
|
||||
return streamID&(1<<31) == 0
|
||||
}
|
||||
|
||||
func validStreamID(streamID uint32) bool {
|
||||
return streamID != 0 && streamID&(1<<31) == 0
|
||||
|
|
@ -940,8 +985,8 @@ func (f *Framer) WriteHeaders(p HeadersFrameParam) error {
|
|||
}
|
||||
if !p.Priority.IsZero() {
|
||||
v := p.Priority.StreamDep
|
||||
if !validStreamID(v) && !f.AllowIllegalWrites {
|
||||
return errors.New("invalid dependent stream id")
|
||||
if !validStreamIDOrZero(v) && !f.AllowIllegalWrites {
|
||||
return errDepStreamID
|
||||
}
|
||||
if p.Priority.Exclusive {
|
||||
v |= 1 << 31
|
||||
|
|
@ -1009,6 +1054,9 @@ func (f *Framer) WritePriority(streamID uint32, p PriorityParam) error {
|
|||
if !validStreamID(streamID) && !f.AllowIllegalWrites {
|
||||
return errStreamID
|
||||
}
|
||||
if !validStreamIDOrZero(p.StreamDep) {
|
||||
return errDepStreamID
|
||||
}
|
||||
f.startWrite(FramePriority, 0, streamID)
|
||||
v := p.StreamDep
|
||||
if p.Exclusive {
|
||||
|
|
@ -1225,6 +1273,196 @@ type headersEnder interface {
|
|||
HeadersEnded() bool
|
||||
}
|
||||
|
||||
type headersOrContinuation interface {
|
||||
headersEnder
|
||||
HeaderBlockFragment() []byte
|
||||
}
|
||||
|
||||
// A MetaHeadersFrame is the representation of one HEADERS frame and
|
||||
// zero or more contiguous CONTINUATION frames and the decoding of
|
||||
// their HPACK-encoded contents.
|
||||
//
|
||||
// This type of frame does not appear on the wire and is only returned
|
||||
// by the Framer when Framer.ReadMetaHeaders is set.
|
||||
type MetaHeadersFrame struct {
|
||||
*HeadersFrame
|
||||
|
||||
// Fields are the fields contained in the HEADERS and
|
||||
// CONTINUATION frames. The underlying slice is owned by the
|
||||
// Framer and must not be retained after the next call to
|
||||
// ReadFrame.
|
||||
//
|
||||
// Fields are guaranteed to be in the correct http2 order and
|
||||
// not have unknown pseudo header fields or invalid header
|
||||
// field names or values. Required pseudo header fields may be
|
||||
// missing, however. Use the MetaHeadersFrame.Pseudo accessor
|
||||
// method access pseudo headers.
|
||||
Fields []hpack.HeaderField
|
||||
|
||||
// Truncated is whether the max header list size limit was hit
|
||||
// and Fields is incomplete. The hpack decoder state is still
|
||||
// valid, however.
|
||||
Truncated bool
|
||||
}
|
||||
|
||||
// PseudoValue returns the given pseudo header field's value.
|
||||
// The provided pseudo field should not contain the leading colon.
|
||||
func (mh *MetaHeadersFrame) PseudoValue(pseudo string) string {
|
||||
for _, hf := range mh.Fields {
|
||||
if !hf.IsPseudo() {
|
||||
return ""
|
||||
}
|
||||
if hf.Name[1:] == pseudo {
|
||||
return hf.Value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// RegularFields returns the regular (non-pseudo) header fields of mh.
|
||||
// The caller does not own the returned slice.
|
||||
func (mh *MetaHeadersFrame) RegularFields() []hpack.HeaderField {
|
||||
for i, hf := range mh.Fields {
|
||||
if !hf.IsPseudo() {
|
||||
return mh.Fields[i:]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PseudoFields returns the pseudo header fields of mh.
|
||||
// The caller does not own the returned slice.
|
||||
func (mh *MetaHeadersFrame) PseudoFields() []hpack.HeaderField {
|
||||
for i, hf := range mh.Fields {
|
||||
if !hf.IsPseudo() {
|
||||
return mh.Fields[:i]
|
||||
}
|
||||
}
|
||||
return mh.Fields
|
||||
}
|
||||
|
||||
func (mh *MetaHeadersFrame) checkPseudos() error {
|
||||
var isRequest, isResponse bool
|
||||
pf := mh.PseudoFields()
|
||||
for i, hf := range pf {
|
||||
switch hf.Name {
|
||||
case ":method", ":path", ":scheme", ":authority":
|
||||
isRequest = true
|
||||
case ":status":
|
||||
isResponse = true
|
||||
default:
|
||||
return pseudoHeaderError(hf.Name)
|
||||
}
|
||||
// Check for duplicates.
|
||||
// This would be a bad algorithm, but N is 4.
|
||||
// And this doesn't allocate.
|
||||
for _, hf2 := range pf[:i] {
|
||||
if hf.Name == hf2.Name {
|
||||
return duplicatePseudoHeaderError(hf.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if isRequest && isResponse {
|
||||
return errMixPseudoHeaderTypes
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fr *Framer) maxHeaderStringLen() int {
|
||||
v := fr.maxHeaderListSize()
|
||||
if uint32(int(v)) == v {
|
||||
return int(v)
|
||||
}
|
||||
// They had a crazy big number for MaxHeaderBytes anyway,
|
||||
// so give them unlimited header lengths:
|
||||
return 0
|
||||
}
|
||||
|
||||
// readMetaFrame returns 0 or more CONTINUATION frames from fr and
|
||||
// merge them into into the provided hf and returns a MetaHeadersFrame
|
||||
// with the decoded hpack values.
|
||||
func (fr *Framer) readMetaFrame(hf *HeadersFrame) (*MetaHeadersFrame, error) {
|
||||
if fr.AllowIllegalReads {
|
||||
return nil, errors.New("illegal use of AllowIllegalReads with ReadMetaHeaders")
|
||||
}
|
||||
mh := &MetaHeadersFrame{
|
||||
HeadersFrame: hf,
|
||||
}
|
||||
var remainSize = fr.maxHeaderListSize()
|
||||
var sawRegular bool
|
||||
|
||||
var invalid error // pseudo header field errors
|
||||
hdec := fr.ReadMetaHeaders
|
||||
hdec.SetEmitEnabled(true)
|
||||
hdec.SetMaxStringLength(fr.maxHeaderStringLen())
|
||||
hdec.SetEmitFunc(func(hf hpack.HeaderField) {
|
||||
if !httplex.ValidHeaderFieldValue(hf.Value) {
|
||||
invalid = headerFieldValueError(hf.Value)
|
||||
}
|
||||
isPseudo := strings.HasPrefix(hf.Name, ":")
|
||||
if isPseudo {
|
||||
if sawRegular {
|
||||
invalid = errPseudoAfterRegular
|
||||
}
|
||||
} else {
|
||||
sawRegular = true
|
||||
if !validWireHeaderFieldName(hf.Name) {
|
||||
invalid = headerFieldNameError(hf.Name)
|
||||
}
|
||||
}
|
||||
|
||||
if invalid != nil {
|
||||
hdec.SetEmitEnabled(false)
|
||||
return
|
||||
}
|
||||
|
||||
size := hf.Size()
|
||||
if size > remainSize {
|
||||
hdec.SetEmitEnabled(false)
|
||||
mh.Truncated = true
|
||||
return
|
||||
}
|
||||
remainSize -= size
|
||||
|
||||
mh.Fields = append(mh.Fields, hf)
|
||||
})
|
||||
// Lose reference to MetaHeadersFrame:
|
||||
defer hdec.SetEmitFunc(func(hf hpack.HeaderField) {})
|
||||
|
||||
var hc headersOrContinuation = hf
|
||||
for {
|
||||
frag := hc.HeaderBlockFragment()
|
||||
if _, err := hdec.Write(frag); err != nil {
|
||||
return nil, ConnectionError(ErrCodeCompression)
|
||||
}
|
||||
|
||||
if hc.HeadersEnded() {
|
||||
break
|
||||
}
|
||||
if f, err := fr.ReadFrame(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
hc = f.(*ContinuationFrame) // guaranteed by checkFrameOrder
|
||||
}
|
||||
}
|
||||
|
||||
mh.HeadersFrame.headerFragBuf = nil
|
||||
mh.HeadersFrame.invalidate()
|
||||
|
||||
if err := hdec.Close(); err != nil {
|
||||
return nil, ConnectionError(ErrCodeCompression)
|
||||
}
|
||||
if invalid != nil {
|
||||
fr.errDetail = invalid
|
||||
return nil, StreamError{mh.StreamID, ErrCodeProtocol}
|
||||
}
|
||||
if err := mh.checkPseudos(); err != nil {
|
||||
fr.errDetail = err
|
||||
return nil, StreamError{mh.StreamID, ErrCodeProtocol}
|
||||
}
|
||||
return mh, nil
|
||||
}
|
||||
|
||||
func summarizeFrame(f Frame) string {
|
||||
var buf bytes.Buffer
|
||||
f.Header().writeDebug(&buf)
|
||||
|
|
|
|||
11
vendor/golang.org/x/net/http2/go15.go
generated
vendored
11
vendor/golang.org/x/net/http2/go15.go
generated
vendored
|
|
@ -1,11 +0,0 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.5
|
||||
|
||||
package http2
|
||||
|
||||
import "net/http"
|
||||
|
||||
func requestCancel(req *http.Request) <-chan struct{} { return req.Cancel }
|
||||
43
vendor/golang.org/x/net/http2/go16.go
generated
vendored
Normal file
43
vendor/golang.org/x/net/http2/go16.go
generated
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.6
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func transportExpectContinueTimeout(t1 *http.Transport) time.Duration {
|
||||
return t1.ExpectContinueTimeout
|
||||
}
|
||||
|
||||
// isBadCipher reports whether the cipher is blacklisted by the HTTP/2 spec.
|
||||
func isBadCipher(cipher uint16) bool {
|
||||
switch cipher {
|
||||
case tls.TLS_RSA_WITH_RC4_128_SHA,
|
||||
tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
|
||||
// Reject cipher suites from Appendix A.
|
||||
// "This list includes those cipher suites that do not
|
||||
// offer an ephemeral key exchange and those that are
|
||||
// based on the TLS null, stream or block cipher type"
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
94
vendor/golang.org/x/net/http2/go17.go
generated
vendored
Normal file
94
vendor/golang.org/x/net/http2/go17.go
generated
vendored
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.7
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptrace"
|
||||
"time"
|
||||
)
|
||||
|
||||
type contextContext interface {
|
||||
context.Context
|
||||
}
|
||||
|
||||
func serverConnBaseContext(c net.Conn, opts *ServeConnOpts) (ctx contextContext, cancel func()) {
|
||||
ctx, cancel = context.WithCancel(context.Background())
|
||||
ctx = context.WithValue(ctx, http.LocalAddrContextKey, c.LocalAddr())
|
||||
if hs := opts.baseConfig(); hs != nil {
|
||||
ctx = context.WithValue(ctx, http.ServerContextKey, hs)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func contextWithCancel(ctx contextContext) (_ contextContext, cancel func()) {
|
||||
return context.WithCancel(ctx)
|
||||
}
|
||||
|
||||
func requestWithContext(req *http.Request, ctx contextContext) *http.Request {
|
||||
return req.WithContext(ctx)
|
||||
}
|
||||
|
||||
type clientTrace httptrace.ClientTrace
|
||||
|
||||
func reqContext(r *http.Request) context.Context { return r.Context() }
|
||||
|
||||
func setResponseUncompressed(res *http.Response) { res.Uncompressed = true }
|
||||
|
||||
func traceGotConn(req *http.Request, cc *ClientConn) {
|
||||
trace := httptrace.ContextClientTrace(req.Context())
|
||||
if trace == nil || trace.GotConn == nil {
|
||||
return
|
||||
}
|
||||
ci := httptrace.GotConnInfo{Conn: cc.tconn}
|
||||
cc.mu.Lock()
|
||||
ci.Reused = cc.nextStreamID > 1
|
||||
ci.WasIdle = len(cc.streams) == 0 && ci.Reused
|
||||
if ci.WasIdle && !cc.lastActive.IsZero() {
|
||||
ci.IdleTime = time.Now().Sub(cc.lastActive)
|
||||
}
|
||||
cc.mu.Unlock()
|
||||
|
||||
trace.GotConn(ci)
|
||||
}
|
||||
|
||||
func traceWroteHeaders(trace *clientTrace) {
|
||||
if trace != nil && trace.WroteHeaders != nil {
|
||||
trace.WroteHeaders()
|
||||
}
|
||||
}
|
||||
|
||||
func traceGot100Continue(trace *clientTrace) {
|
||||
if trace != nil && trace.Got100Continue != nil {
|
||||
trace.Got100Continue()
|
||||
}
|
||||
}
|
||||
|
||||
func traceWait100Continue(trace *clientTrace) {
|
||||
if trace != nil && trace.Wait100Continue != nil {
|
||||
trace.Wait100Continue()
|
||||
}
|
||||
}
|
||||
|
||||
func traceWroteRequest(trace *clientTrace, err error) {
|
||||
if trace != nil && trace.WroteRequest != nil {
|
||||
trace.WroteRequest(httptrace.WroteRequestInfo{Err: err})
|
||||
}
|
||||
}
|
||||
|
||||
func traceFirstResponseByte(trace *clientTrace) {
|
||||
if trace != nil && trace.GotFirstResponseByte != nil {
|
||||
trace.GotFirstResponseByte()
|
||||
}
|
||||
}
|
||||
|
||||
func requestTrace(req *http.Request) *clientTrace {
|
||||
trace := httptrace.ContextClientTrace(req.Context())
|
||||
return (*clientTrace)(trace)
|
||||
}
|
||||
2
vendor/golang.org/x/net/http2/hpack/encode.go
generated
vendored
2
vendor/golang.org/x/net/http2/hpack/encode.go
generated
vendored
|
|
@ -144,7 +144,7 @@ func (e *Encoder) SetMaxDynamicTableSizeLimit(v uint32) {
|
|||
|
||||
// shouldIndex reports whether f should be indexed.
|
||||
func (e *Encoder) shouldIndex(f HeaderField) bool {
|
||||
return !f.Sensitive && f.size() <= e.dynTab.maxSize
|
||||
return !f.Sensitive && f.Size() <= e.dynTab.maxSize
|
||||
}
|
||||
|
||||
// appendIndexed appends index i, as encoded in "Indexed Header Field"
|
||||
|
|
|
|||
15
vendor/golang.org/x/net/http2/hpack/hpack.go
generated
vendored
15
vendor/golang.org/x/net/http2/hpack/hpack.go
generated
vendored
|
|
@ -41,6 +41,14 @@ type HeaderField struct {
|
|||
Sensitive bool
|
||||
}
|
||||
|
||||
// IsPseudo reports whether the header field is an http2 pseudo header.
|
||||
// That is, it reports whether it starts with a colon.
|
||||
// It is not otherwise guaranteed to be a valid pseudo header field,
|
||||
// though.
|
||||
func (hf HeaderField) IsPseudo() bool {
|
||||
return len(hf.Name) != 0 && hf.Name[0] == ':'
|
||||
}
|
||||
|
||||
func (hf HeaderField) String() string {
|
||||
var suffix string
|
||||
if hf.Sensitive {
|
||||
|
|
@ -49,7 +57,8 @@ func (hf HeaderField) String() string {
|
|||
return fmt.Sprintf("header field %q = %q%s", hf.Name, hf.Value, suffix)
|
||||
}
|
||||
|
||||
func (hf *HeaderField) size() uint32 {
|
||||
// Size returns the size of an entry per RFC 7540 section 5.2.
|
||||
func (hf HeaderField) Size() uint32 {
|
||||
// http://http2.github.io/http2-spec/compression.html#rfc.section.4.1
|
||||
// "The size of the dynamic table is the sum of the size of
|
||||
// its entries. The size of an entry is the sum of its name's
|
||||
|
|
@ -171,7 +180,7 @@ func (dt *dynamicTable) setMaxSize(v uint32) {
|
|||
|
||||
func (dt *dynamicTable) add(f HeaderField) {
|
||||
dt.ents = append(dt.ents, f)
|
||||
dt.size += f.size()
|
||||
dt.size += f.Size()
|
||||
dt.evict()
|
||||
}
|
||||
|
||||
|
|
@ -179,7 +188,7 @@ func (dt *dynamicTable) add(f HeaderField) {
|
|||
func (dt *dynamicTable) evict() {
|
||||
base := dt.ents // keep base pointer of slice
|
||||
for dt.size > dt.maxSize {
|
||||
dt.size -= dt.ents[0].size()
|
||||
dt.size -= dt.ents[0].Size()
|
||||
dt.ents = dt.ents[1:]
|
||||
}
|
||||
|
||||
|
|
|
|||
42
vendor/golang.org/x/net/http2/hpack/huffman.go
generated
vendored
42
vendor/golang.org/x/net/http2/hpack/huffman.go
generated
vendored
|
|
@ -48,12 +48,16 @@ var ErrInvalidHuffman = errors.New("hpack: invalid Huffman-encoded data")
|
|||
// maxLen bytes will return ErrStringLength.
|
||||
func huffmanDecode(buf *bytes.Buffer, maxLen int, v []byte) error {
|
||||
n := rootHuffmanNode
|
||||
cur, nbits := uint(0), uint8(0)
|
||||
// cur is the bit buffer that has not been fed into n.
|
||||
// cbits is the number of low order bits in cur that are valid.
|
||||
// sbits is the number of bits of the symbol prefix being decoded.
|
||||
cur, cbits, sbits := uint(0), uint8(0), uint8(0)
|
||||
for _, b := range v {
|
||||
cur = cur<<8 | uint(b)
|
||||
nbits += 8
|
||||
for nbits >= 8 {
|
||||
idx := byte(cur >> (nbits - 8))
|
||||
cbits += 8
|
||||
sbits += 8
|
||||
for cbits >= 8 {
|
||||
idx := byte(cur >> (cbits - 8))
|
||||
n = n.children[idx]
|
||||
if n == nil {
|
||||
return ErrInvalidHuffman
|
||||
|
|
@ -63,22 +67,40 @@ func huffmanDecode(buf *bytes.Buffer, maxLen int, v []byte) error {
|
|||
return ErrStringLength
|
||||
}
|
||||
buf.WriteByte(n.sym)
|
||||
nbits -= n.codeLen
|
||||
cbits -= n.codeLen
|
||||
n = rootHuffmanNode
|
||||
sbits = cbits
|
||||
} else {
|
||||
nbits -= 8
|
||||
cbits -= 8
|
||||
}
|
||||
}
|
||||
}
|
||||
for nbits > 0 {
|
||||
n = n.children[byte(cur<<(8-nbits))]
|
||||
if n.children != nil || n.codeLen > nbits {
|
||||
for cbits > 0 {
|
||||
n = n.children[byte(cur<<(8-cbits))]
|
||||
if n == nil {
|
||||
return ErrInvalidHuffman
|
||||
}
|
||||
if n.children != nil || n.codeLen > cbits {
|
||||
break
|
||||
}
|
||||
if maxLen != 0 && buf.Len() == maxLen {
|
||||
return ErrStringLength
|
||||
}
|
||||
buf.WriteByte(n.sym)
|
||||
nbits -= n.codeLen
|
||||
cbits -= n.codeLen
|
||||
n = rootHuffmanNode
|
||||
sbits = cbits
|
||||
}
|
||||
if sbits > 7 {
|
||||
// Either there was an incomplete symbol, or overlong padding.
|
||||
// Both are decoding errors per RFC 7541 section 5.2.
|
||||
return ErrInvalidHuffman
|
||||
}
|
||||
if mask := uint(1<<cbits - 1); cur&mask != mask {
|
||||
// Trailing bits must be a prefix of EOS per RFC 7541 section 5.2.
|
||||
return ErrInvalidHuffman
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
164
vendor/golang.org/x/net/http2/http2.go
generated
vendored
164
vendor/golang.org/x/net/http2/http2.go
generated
vendored
|
|
@ -23,9 +23,12 @@ import (
|
|||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/net/lex/httplex"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -165,57 +168,23 @@ var (
|
|||
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 = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." /
|
||||
// "^" / "_" / "
|
||||
// validWireHeaderFieldName reports whether v is a valid header field
|
||||
// name (key). See httplex.ValidHeaderName for the base rules.
|
||||
//
|
||||
// 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 {
|
||||
func validWireHeaderFieldName(v string) bool {
|
||||
if len(v) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, r := range v {
|
||||
if int(r) >= len(isTokenTable) || ('A' <= r && r <= 'Z') {
|
||||
if !httplex.IsTokenRune(r) {
|
||||
return false
|
||||
}
|
||||
if !isTokenTable[byte(r)] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
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 {
|
||||
if 'A' <= r && r <= 'Z' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
@ -320,7 +289,7 @@ func mustUint31(v int32) uint32 {
|
|||
}
|
||||
|
||||
// bodyAllowedForStatus reports whether a given response status code
|
||||
// permits a body. See RFC2616, section 4.4.
|
||||
// permits a body. See RFC 2616, section 4.4.
|
||||
func bodyAllowedForStatus(status int) bool {
|
||||
switch {
|
||||
case status >= 100 && status <= 199:
|
||||
|
|
@ -344,86 +313,39 @@ 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
|
||||
}
|
||||
|
||||
var sorterPool = sync.Pool{New: func() interface{} { return new(sorter) }}
|
||||
|
||||
type sorter struct {
|
||||
v []string // owned by sorter
|
||||
}
|
||||
|
||||
func (s *sorter) Len() int { return len(s.v) }
|
||||
func (s *sorter) Swap(i, j int) { s.v[i], s.v[j] = s.v[j], s.v[i] }
|
||||
func (s *sorter) Less(i, j int) bool { return s.v[i] < s.v[j] }
|
||||
|
||||
// Keys returns the sorted keys of h.
|
||||
//
|
||||
// The returned slice is only valid until s used again or returned to
|
||||
// its pool.
|
||||
func (s *sorter) Keys(h http.Header) []string {
|
||||
keys := s.v[:0]
|
||||
for k := range h {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
s.v = keys
|
||||
sort.Sort(s)
|
||||
return keys
|
||||
}
|
||||
|
||||
func (s *sorter) SortStrings(ss []string) {
|
||||
// Our sorter works on s.v, which sorter owners, so
|
||||
// stash it away while we sort the user's buffer.
|
||||
save := s.v
|
||||
s.v = ss
|
||||
sort.Sort(s)
|
||||
s.v = save
|
||||
}
|
||||
|
|
|
|||
11
vendor/golang.org/x/net/http2/not_go15.go
generated
vendored
11
vendor/golang.org/x/net/http2/not_go15.go
generated
vendored
|
|
@ -1,11 +0,0 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !go1.5
|
||||
|
||||
package http2
|
||||
|
||||
import "net/http"
|
||||
|
||||
func requestCancel(req *http.Request) <-chan struct{} { return nil }
|
||||
35
vendor/golang.org/x/net/http2/not_go16.go
generated
vendored
35
vendor/golang.org/x/net/http2/not_go16.go
generated
vendored
|
|
@ -6,8 +6,41 @@
|
|||
|
||||
package http2
|
||||
|
||||
import "net/http"
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func configureTransport(t1 *http.Transport) (*Transport, error) {
|
||||
return nil, errTransportVersion
|
||||
}
|
||||
|
||||
func transportExpectContinueTimeout(t1 *http.Transport) time.Duration {
|
||||
return 0
|
||||
|
||||
}
|
||||
|
||||
// isBadCipher reports whether the cipher is blacklisted by the HTTP/2 spec.
|
||||
func isBadCipher(cipher uint16) bool {
|
||||
switch cipher {
|
||||
case tls.TLS_RSA_WITH_RC4_128_SHA,
|
||||
tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
|
||||
// Reject cipher suites from Appendix A.
|
||||
// "This list includes those cipher suites that do not
|
||||
// offer an ephemeral key exchange and those that are
|
||||
// based on the TLS null, stream or block cipher type"
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
51
vendor/golang.org/x/net/http2/not_go17.go
generated
vendored
Normal file
51
vendor/golang.org/x/net/http2/not_go17.go
generated
vendored
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !go1.7
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type contextContext interface{}
|
||||
|
||||
type fakeContext struct{}
|
||||
|
||||
func (fakeContext) Done() <-chan struct{} { return nil }
|
||||
func (fakeContext) Err() error { panic("should not be called") }
|
||||
|
||||
func reqContext(r *http.Request) fakeContext {
|
||||
return fakeContext{}
|
||||
}
|
||||
|
||||
func setResponseUncompressed(res *http.Response) {
|
||||
// Nothing.
|
||||
}
|
||||
|
||||
type clientTrace struct{}
|
||||
|
||||
func requestTrace(*http.Request) *clientTrace { return nil }
|
||||
func traceGotConn(*http.Request, *ClientConn) {}
|
||||
func traceFirstResponseByte(*clientTrace) {}
|
||||
func traceWroteHeaders(*clientTrace) {}
|
||||
func traceWroteRequest(*clientTrace, error) {}
|
||||
func traceGot100Continue(trace *clientTrace) {}
|
||||
func traceWait100Continue(trace *clientTrace) {}
|
||||
|
||||
func nop() {}
|
||||
|
||||
func serverConnBaseContext(c net.Conn, opts *ServeConnOpts) (ctx contextContext, cancel func()) {
|
||||
return nil, nop
|
||||
}
|
||||
|
||||
func contextWithCancel(ctx contextContext) (_ contextContext, cancel func()) {
|
||||
return ctx, nop
|
||||
}
|
||||
|
||||
func requestWithContext(req *http.Request, ctx contextContext) *http.Request {
|
||||
return req
|
||||
}
|
||||
6
vendor/golang.org/x/net/http2/pipe.go
generated
vendored
6
vendor/golang.org/x/net/http2/pipe.go
generated
vendored
|
|
@ -29,6 +29,12 @@ type pipeBuffer interface {
|
|||
io.Reader
|
||||
}
|
||||
|
||||
func (p *pipe) Len() int {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
return p.b.Len()
|
||||
}
|
||||
|
||||
// Read waits until data is available and copies bytes
|
||||
// from the buffer into p.
|
||||
func (p *pipe) Read(d []byte) (n int, err error) {
|
||||
|
|
|
|||
473
vendor/golang.org/x/net/http2/server.go
generated
vendored
473
vendor/golang.org/x/net/http2/server.go
generated
vendored
|
|
@ -51,7 +51,6 @@ import (
|
|||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
|
@ -251,10 +250,14 @@ func (o *ServeConnOpts) handler() http.Handler {
|
|||
//
|
||||
// The opts parameter is optional. If nil, default values are used.
|
||||
func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
|
||||
baseCtx, cancel := serverConnBaseContext(c, opts)
|
||||
defer cancel()
|
||||
|
||||
sc := &serverConn{
|
||||
srv: s,
|
||||
hs: opts.baseConfig(),
|
||||
conn: c,
|
||||
baseCtx: baseCtx,
|
||||
remoteAddrStr: c.RemoteAddr().String(),
|
||||
bw: newBufferedWriter(c),
|
||||
handler: opts.handler(),
|
||||
|
|
@ -273,13 +276,14 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
|
|||
serveG: newGoroutineLock(),
|
||||
pushEnabled: true,
|
||||
}
|
||||
|
||||
sc.flow.add(initialWindowSize)
|
||||
sc.inflow.add(initialWindowSize)
|
||||
sc.hpackEncoder = hpack.NewEncoder(&sc.headerWriteBuf)
|
||||
sc.hpackDecoder = hpack.NewDecoder(initialHeaderTableSize, nil)
|
||||
sc.hpackDecoder.SetMaxStringLength(sc.maxHeaderStringLen())
|
||||
|
||||
fr := NewFramer(sc.bw, c)
|
||||
fr.ReadMetaHeaders = hpack.NewDecoder(initialHeaderTableSize, nil)
|
||||
fr.MaxHeaderListSize = sc.maxHeaderListSize()
|
||||
fr.SetMaxReadFrameSize(s.maxReadFrameSize())
|
||||
sc.framer = fr
|
||||
|
||||
|
|
@ -335,30 +339,6 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
|
|||
sc.serve()
|
||||
}
|
||||
|
||||
// isBadCipher reports whether the cipher is blacklisted by the HTTP/2 spec.
|
||||
func isBadCipher(cipher uint16) bool {
|
||||
switch cipher {
|
||||
case tls.TLS_RSA_WITH_RC4_128_SHA,
|
||||
tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
|
||||
// Reject cipher suites from Appendix A.
|
||||
// "This list includes those cipher suites that do not
|
||||
// offer an ephemeral key exchange and those that are
|
||||
// based on the TLS null, stream or block cipher type"
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (sc *serverConn) rejectConn(err ErrCode, debug string) {
|
||||
sc.vlogf("http2: server rejecting conn: %v, %s", err, debug)
|
||||
// ignoring errors. hanging up anyway.
|
||||
|
|
@ -374,8 +354,8 @@ type serverConn struct {
|
|||
conn net.Conn
|
||||
bw *bufferedWriter // writing to conn
|
||||
handler http.Handler
|
||||
baseCtx contextContext
|
||||
framer *Framer
|
||||
hpackDecoder *hpack.Decoder
|
||||
doneServing chan struct{} // closed when serverConn.serve ends
|
||||
readFrameCh chan readFrameResult // written by serverConn.readFrames
|
||||
wantWriteFrameCh chan frameWriteMsg // from handlers -> serve
|
||||
|
|
@ -402,7 +382,6 @@ type serverConn struct {
|
|||
headerTableSize uint32
|
||||
peerMaxHeaderListSize uint32 // zero means unknown (default)
|
||||
canonHeader map[string]string // http2-lower-case -> Go-Canonical-Case
|
||||
req requestParam // non-zero while reading request headers
|
||||
writingFrame bool // started write goroutine but haven't heard back on wroteFrameCh
|
||||
needsFrameFlush bool // last frame write wasn't a flush
|
||||
writeSched writeScheduler
|
||||
|
|
@ -411,22 +390,13 @@ type serverConn struct {
|
|||
goAwayCode ErrCode
|
||||
shutdownTimerCh <-chan time.Time // nil until used
|
||||
shutdownTimer *time.Timer // nil until used
|
||||
freeRequestBodyBuf []byte // if non-nil, a free initialWindowSize buffer for getRequestBodyBuf
|
||||
|
||||
// Owned by the writeFrameAsync goroutine:
|
||||
headerWriteBuf bytes.Buffer
|
||||
hpackEncoder *hpack.Encoder
|
||||
}
|
||||
|
||||
func (sc *serverConn) maxHeaderStringLen() int {
|
||||
v := sc.maxHeaderListSize()
|
||||
if uint32(int(v)) == v {
|
||||
return int(v)
|
||||
}
|
||||
// They had a crazy big number for MaxHeaderBytes anyway,
|
||||
// so give them unlimited header lengths:
|
||||
return 0
|
||||
}
|
||||
|
||||
func (sc *serverConn) maxHeaderListSize() uint32 {
|
||||
n := sc.hs.MaxHeaderBytes
|
||||
if n <= 0 {
|
||||
|
|
@ -439,21 +409,6 @@ func (sc *serverConn) maxHeaderListSize() uint32 {
|
|||
return uint32(n + typicalHeaders*perFieldOverhead)
|
||||
}
|
||||
|
||||
// requestParam is the state of the next request, initialized over
|
||||
// potentially several frames HEADERS + zero or more CONTINUATION
|
||||
// frames.
|
||||
type requestParam struct {
|
||||
// stream is non-nil if we're reading (HEADER or CONTINUATION)
|
||||
// frames for a request (but not DATA).
|
||||
stream *stream
|
||||
header http.Header
|
||||
method, path string
|
||||
scheme, authority string
|
||||
sawRegularHeader bool // saw a non-pseudo header already
|
||||
invalidHeader bool // an invalid header was seen
|
||||
headerListSize int64 // actually uint32, but easier math this way
|
||||
}
|
||||
|
||||
// stream represents a stream. This is the minimal metadata needed by
|
||||
// the serve goroutine. Most of the actual stream state is owned by
|
||||
// the http.Handler's goroutine in the responseWriter. Because the
|
||||
|
|
@ -463,10 +418,12 @@ type requestParam struct {
|
|||
// responseWriter's state field.
|
||||
type stream struct {
|
||||
// immutable:
|
||||
sc *serverConn
|
||||
id uint32
|
||||
body *pipe // non-nil if expecting DATA frames
|
||||
cw closeWaiter // closed wait stream transitions to closed state
|
||||
sc *serverConn
|
||||
id uint32
|
||||
body *pipe // non-nil if expecting DATA frames
|
||||
cw closeWaiter // closed wait stream transitions to closed state
|
||||
ctx contextContext
|
||||
cancelCtx func()
|
||||
|
||||
// owned by serverConn's serve loop:
|
||||
bodyBytes int64 // body bytes seen so far
|
||||
|
|
@ -480,6 +437,8 @@ type stream struct {
|
|||
sentReset bool // only true once detached from streams map
|
||||
gotReset bool // only true once detacted from streams map
|
||||
gotTrailerHeader bool // HEADER frame for trailers was seen
|
||||
wroteHeaders bool // whether we wrote headers (not status 100)
|
||||
reqBuf []byte
|
||||
|
||||
trailer http.Header // accumulated trailers
|
||||
reqTrailer http.Header // handler's Request.Trailer
|
||||
|
|
@ -589,87 +548,6 @@ func (sc *serverConn) condlogf(err error, format string, args ...interface{}) {
|
|||
}
|
||||
}
|
||||
|
||||
func (sc *serverConn) onNewHeaderField(f hpack.HeaderField) {
|
||||
sc.serveG.check()
|
||||
if VerboseLogs {
|
||||
sc.vlogf("http2: server decoded %v", f)
|
||||
}
|
||||
switch {
|
||||
case !validHeaderFieldValue(f.Value): // f.Name checked _after_ pseudo check, since ':' is invalid
|
||||
sc.req.invalidHeader = true
|
||||
case strings.HasPrefix(f.Name, ":"):
|
||||
if sc.req.sawRegularHeader {
|
||||
sc.logf("pseudo-header after regular header")
|
||||
sc.req.invalidHeader = true
|
||||
return
|
||||
}
|
||||
var dst *string
|
||||
switch f.Name {
|
||||
case ":method":
|
||||
dst = &sc.req.method
|
||||
case ":path":
|
||||
dst = &sc.req.path
|
||||
case ":scheme":
|
||||
dst = &sc.req.scheme
|
||||
case ":authority":
|
||||
dst = &sc.req.authority
|
||||
default:
|
||||
// 8.1.2.1 Pseudo-Header Fields
|
||||
// "Endpoints MUST treat a request or response
|
||||
// that contains undefined or invalid
|
||||
// pseudo-header fields as malformed (Section
|
||||
// 8.1.2.6)."
|
||||
sc.logf("invalid pseudo-header %q", f.Name)
|
||||
sc.req.invalidHeader = true
|
||||
return
|
||||
}
|
||||
if *dst != "" {
|
||||
sc.logf("duplicate pseudo-header %q sent", f.Name)
|
||||
sc.req.invalidHeader = true
|
||||
return
|
||||
}
|
||||
*dst = f.Value
|
||||
case !validHeaderFieldName(f.Name):
|
||||
sc.req.invalidHeader = true
|
||||
default:
|
||||
sc.req.sawRegularHeader = true
|
||||
sc.req.header.Add(sc.canonicalHeader(f.Name), f.Value)
|
||||
const headerFieldOverhead = 32 // per spec
|
||||
sc.req.headerListSize += int64(len(f.Name)) + int64(len(f.Value)) + headerFieldOverhead
|
||||
if sc.req.headerListSize > int64(sc.maxHeaderListSize()) {
|
||||
sc.hpackDecoder.SetEmitEnabled(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (st *stream) onNewTrailerField(f hpack.HeaderField) {
|
||||
sc := st.sc
|
||||
sc.serveG.check()
|
||||
if VerboseLogs {
|
||||
sc.vlogf("http2: server decoded trailer %v", f)
|
||||
}
|
||||
switch {
|
||||
case strings.HasPrefix(f.Name, ":"):
|
||||
sc.req.invalidHeader = true
|
||||
return
|
||||
case !validHeaderFieldName(f.Name) || !validHeaderFieldValue(f.Value):
|
||||
sc.req.invalidHeader = true
|
||||
return
|
||||
default:
|
||||
key := sc.canonicalHeader(f.Name)
|
||||
if st.trailer != nil {
|
||||
vv := append(st.trailer[key], f.Value)
|
||||
st.trailer[key] = vv
|
||||
|
||||
// arbitrary; TODO: read spec about header list size limits wrt trailers
|
||||
const tooBig = 1000
|
||||
if len(vv) >= tooBig {
|
||||
sc.hpackDecoder.SetEmitEnabled(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (sc *serverConn) canonicalHeader(v string) string {
|
||||
sc.serveG.check()
|
||||
cv, ok := commonCanonHeader[v]
|
||||
|
|
@ -704,10 +582,11 @@ type readFrameResult struct {
|
|||
// It's run on its own goroutine.
|
||||
func (sc *serverConn) readFrames() {
|
||||
gate := make(gate)
|
||||
gateDone := gate.Done
|
||||
for {
|
||||
f, err := sc.framer.ReadFrame()
|
||||
select {
|
||||
case sc.readFrameCh <- readFrameResult{f, err, gate.Done}:
|
||||
case sc.readFrameCh <- readFrameResult{f, err, gateDone}:
|
||||
case <-sc.doneServing:
|
||||
return
|
||||
}
|
||||
|
|
@ -946,7 +825,23 @@ func (sc *serverConn) writeFrameFromHandler(wm frameWriteMsg) error {
|
|||
// If you're not on the serve goroutine, use writeFrameFromHandler instead.
|
||||
func (sc *serverConn) writeFrame(wm frameWriteMsg) {
|
||||
sc.serveG.check()
|
||||
sc.writeSched.add(wm)
|
||||
|
||||
var ignoreWrite bool
|
||||
|
||||
// Don't send a 100-continue response if we've already sent headers.
|
||||
// See golang.org/issue/14030.
|
||||
switch wm.write.(type) {
|
||||
case *writeResHeaders:
|
||||
wm.stream.wroteHeaders = true
|
||||
case write100ContinueHeadersFrame:
|
||||
if wm.stream.wroteHeaders {
|
||||
ignoreWrite = true
|
||||
}
|
||||
}
|
||||
|
||||
if !ignoreWrite {
|
||||
sc.writeSched.add(wm)
|
||||
}
|
||||
sc.scheduleFrameWrite()
|
||||
}
|
||||
|
||||
|
|
@ -1183,10 +1078,8 @@ func (sc *serverConn) processFrame(f Frame) error {
|
|||
switch f := f.(type) {
|
||||
case *SettingsFrame:
|
||||
return sc.processSettings(f)
|
||||
case *HeadersFrame:
|
||||
case *MetaHeadersFrame:
|
||||
return sc.processHeaders(f)
|
||||
case *ContinuationFrame:
|
||||
return sc.processContinuation(f)
|
||||
case *WindowUpdateFrame:
|
||||
return sc.processWindowUpdate(f)
|
||||
case *PingFrame:
|
||||
|
|
@ -1265,6 +1158,7 @@ func (sc *serverConn) processResetStream(f *RSTStreamFrame) error {
|
|||
}
|
||||
if st != nil {
|
||||
st.gotReset = true
|
||||
st.cancelCtx()
|
||||
sc.closeStream(st, StreamError{f.StreamID, f.ErrCode})
|
||||
}
|
||||
return nil
|
||||
|
|
@ -1286,6 +1180,18 @@ func (sc *serverConn) closeStream(st *stream, err error) {
|
|||
}
|
||||
st.cw.Close() // signals Handler's CloseNotifier, unblocks writes, etc
|
||||
sc.writeSched.forgetStream(st.id)
|
||||
if st.reqBuf != nil {
|
||||
// Stash this request body buffer (64k) away for reuse
|
||||
// by a future POST/PUT/etc.
|
||||
//
|
||||
// TODO(bradfitz): share on the server? sync.Pool?
|
||||
// Server requires locks and might hurt contention.
|
||||
// sync.Pool might work, or might be worse, depending
|
||||
// on goroutine CPU migrations. (get and put on
|
||||
// separate CPUs). Maybe a mix of strategies. But
|
||||
// this is an easy win for now.
|
||||
sc.freeRequestBodyBuf = st.reqBuf
|
||||
}
|
||||
}
|
||||
|
||||
func (sc *serverConn) processSettings(f *SettingsFrame) error {
|
||||
|
|
@ -1442,7 +1348,7 @@ func (st *stream) copyTrailersToHandlerRequest() {
|
|||
}
|
||||
}
|
||||
|
||||
func (sc *serverConn) processHeaders(f *HeadersFrame) error {
|
||||
func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
|
||||
sc.serveG.check()
|
||||
id := f.Header().StreamID
|
||||
if sc.inGoAway {
|
||||
|
|
@ -1471,17 +1377,18 @@ func (sc *serverConn) processHeaders(f *HeadersFrame) error {
|
|||
// endpoint has opened or reserved. [...] An endpoint that
|
||||
// receives an unexpected stream identifier MUST respond with
|
||||
// a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
|
||||
if id <= sc.maxStreamID || sc.req.stream != nil {
|
||||
if id <= sc.maxStreamID {
|
||||
return ConnectionError(ErrCodeProtocol)
|
||||
}
|
||||
sc.maxStreamID = id
|
||||
|
||||
if id > sc.maxStreamID {
|
||||
sc.maxStreamID = id
|
||||
}
|
||||
ctx, cancelCtx := contextWithCancel(sc.baseCtx)
|
||||
st = &stream{
|
||||
sc: sc,
|
||||
id: id,
|
||||
state: stateOpen,
|
||||
sc: sc,
|
||||
id: id,
|
||||
state: stateOpen,
|
||||
ctx: ctx,
|
||||
cancelCtx: cancelCtx,
|
||||
}
|
||||
if f.StreamEnded() {
|
||||
st.state = stateHalfClosedRemote
|
||||
|
|
@ -1501,50 +1408,6 @@ func (sc *serverConn) processHeaders(f *HeadersFrame) error {
|
|||
if sc.curOpenStreams == 1 {
|
||||
sc.setConnState(http.StateActive)
|
||||
}
|
||||
sc.req = requestParam{
|
||||
stream: st,
|
||||
header: make(http.Header),
|
||||
}
|
||||
sc.hpackDecoder.SetEmitFunc(sc.onNewHeaderField)
|
||||
sc.hpackDecoder.SetEmitEnabled(true)
|
||||
return sc.processHeaderBlockFragment(st, f.HeaderBlockFragment(), f.HeadersEnded())
|
||||
}
|
||||
|
||||
func (st *stream) processTrailerHeaders(f *HeadersFrame) error {
|
||||
sc := st.sc
|
||||
sc.serveG.check()
|
||||
if st.gotTrailerHeader {
|
||||
return ConnectionError(ErrCodeProtocol)
|
||||
}
|
||||
st.gotTrailerHeader = true
|
||||
if !f.StreamEnded() {
|
||||
return StreamError{st.id, ErrCodeProtocol}
|
||||
}
|
||||
sc.resetPendingRequest() // we use invalidHeader from it for trailers
|
||||
return st.processTrailerHeaderBlockFragment(f.HeaderBlockFragment(), f.HeadersEnded())
|
||||
}
|
||||
|
||||
func (sc *serverConn) processContinuation(f *ContinuationFrame) error {
|
||||
sc.serveG.check()
|
||||
st := sc.streams[f.Header().StreamID]
|
||||
if st.gotTrailerHeader {
|
||||
return st.processTrailerHeaderBlockFragment(f.HeaderBlockFragment(), f.HeadersEnded())
|
||||
}
|
||||
return sc.processHeaderBlockFragment(st, f.HeaderBlockFragment(), f.HeadersEnded())
|
||||
}
|
||||
|
||||
func (sc *serverConn) processHeaderBlockFragment(st *stream, frag []byte, end bool) error {
|
||||
sc.serveG.check()
|
||||
if _, err := sc.hpackDecoder.Write(frag); err != nil {
|
||||
return ConnectionError(ErrCodeCompression)
|
||||
}
|
||||
if !end {
|
||||
return nil
|
||||
}
|
||||
if err := sc.hpackDecoder.Close(); err != nil {
|
||||
return ConnectionError(ErrCodeCompression)
|
||||
}
|
||||
defer sc.resetPendingRequest()
|
||||
if sc.curOpenStreams > sc.advMaxStreams {
|
||||
// "Endpoints MUST NOT exceed the limit set by their
|
||||
// peer. An endpoint that receives a HEADERS frame
|
||||
|
|
@ -1564,7 +1427,7 @@ func (sc *serverConn) processHeaderBlockFragment(st *stream, frag []byte, end bo
|
|||
return StreamError{st.id, ErrCodeRefusedStream}
|
||||
}
|
||||
|
||||
rw, req, err := sc.newWriterAndRequest()
|
||||
rw, req, err := sc.newWriterAndRequest(st, f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -1576,36 +1439,44 @@ func (sc *serverConn) processHeaderBlockFragment(st *stream, frag []byte, end bo
|
|||
st.declBodyBytes = req.ContentLength
|
||||
|
||||
handler := sc.handler.ServeHTTP
|
||||
if !sc.hpackDecoder.EmitEnabled() {
|
||||
if f.Truncated {
|
||||
// Their header list was too long. Send a 431 error.
|
||||
handler = handleHeaderListTooLong
|
||||
} else if err := checkValidHTTP2Request(req); err != nil {
|
||||
handler = new400Handler(err)
|
||||
}
|
||||
|
||||
go sc.runHandler(rw, req, handler)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (st *stream) processTrailerHeaderBlockFragment(frag []byte, end bool) error {
|
||||
func (st *stream) processTrailerHeaders(f *MetaHeadersFrame) error {
|
||||
sc := st.sc
|
||||
sc.serveG.check()
|
||||
sc.hpackDecoder.SetEmitFunc(st.onNewTrailerField)
|
||||
if _, err := sc.hpackDecoder.Write(frag); err != nil {
|
||||
return ConnectionError(ErrCodeCompression)
|
||||
if st.gotTrailerHeader {
|
||||
return ConnectionError(ErrCodeProtocol)
|
||||
}
|
||||
if !end {
|
||||
return nil
|
||||
st.gotTrailerHeader = true
|
||||
if !f.StreamEnded() {
|
||||
return StreamError{st.id, ErrCodeProtocol}
|
||||
}
|
||||
|
||||
rp := &sc.req
|
||||
if rp.invalidHeader {
|
||||
return StreamError{rp.stream.id, ErrCodeProtocol}
|
||||
if len(f.PseudoFields()) > 0 {
|
||||
return StreamError{st.id, ErrCodeProtocol}
|
||||
}
|
||||
if st.trailer != nil {
|
||||
for _, hf := range f.RegularFields() {
|
||||
key := sc.canonicalHeader(hf.Name)
|
||||
if !ValidTrailerHeader(key) {
|
||||
// TODO: send more details to the peer somehow. But http2 has
|
||||
// no way to send debug data at a stream level. Discuss with
|
||||
// HTTP folk.
|
||||
return StreamError{st.id, ErrCodeProtocol}
|
||||
}
|
||||
st.trailer[key] = append(st.trailer[key], hf.Value)
|
||||
}
|
||||
}
|
||||
|
||||
err := sc.hpackDecoder.Close()
|
||||
st.endStream()
|
||||
if err != nil {
|
||||
return ConnectionError(ErrCodeCompression)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -1650,29 +1521,21 @@ func adjustStreamPriority(streams map[uint32]*stream, streamID uint32, priority
|
|||
}
|
||||
}
|
||||
|
||||
// resetPendingRequest zeros out all state related to a HEADERS frame
|
||||
// and its zero or more CONTINUATION frames sent to start a new
|
||||
// request.
|
||||
func (sc *serverConn) resetPendingRequest() {
|
||||
func (sc *serverConn) newWriterAndRequest(st *stream, f *MetaHeadersFrame) (*responseWriter, *http.Request, error) {
|
||||
sc.serveG.check()
|
||||
sc.req = requestParam{}
|
||||
}
|
||||
|
||||
func (sc *serverConn) newWriterAndRequest() (*responseWriter, *http.Request, error) {
|
||||
sc.serveG.check()
|
||||
rp := &sc.req
|
||||
method := f.PseudoValue("method")
|
||||
path := f.PseudoValue("path")
|
||||
scheme := f.PseudoValue("scheme")
|
||||
authority := f.PseudoValue("authority")
|
||||
|
||||
if rp.invalidHeader {
|
||||
return nil, nil, StreamError{rp.stream.id, ErrCodeProtocol}
|
||||
}
|
||||
|
||||
isConnect := rp.method == "CONNECT"
|
||||
isConnect := method == "CONNECT"
|
||||
if isConnect {
|
||||
if rp.path != "" || rp.scheme != "" || rp.authority == "" {
|
||||
return nil, nil, StreamError{rp.stream.id, ErrCodeProtocol}
|
||||
if path != "" || scheme != "" || authority == "" {
|
||||
return nil, nil, StreamError{f.StreamID, ErrCodeProtocol}
|
||||
}
|
||||
} else if rp.method == "" || rp.path == "" ||
|
||||
(rp.scheme != "https" && rp.scheme != "http") {
|
||||
} else if method == "" || path == "" ||
|
||||
(scheme != "https" && scheme != "http") {
|
||||
// See 8.1.2.6 Malformed Requests and Responses:
|
||||
//
|
||||
// Malformed requests or responses that are detected
|
||||
|
|
@ -1683,35 +1546,40 @@ func (sc *serverConn) newWriterAndRequest() (*responseWriter, *http.Request, err
|
|||
// "All HTTP/2 requests MUST include exactly one valid
|
||||
// value for the :method, :scheme, and :path
|
||||
// pseudo-header fields"
|
||||
return nil, nil, StreamError{rp.stream.id, ErrCodeProtocol}
|
||||
return nil, nil, StreamError{f.StreamID, ErrCodeProtocol}
|
||||
}
|
||||
|
||||
bodyOpen := rp.stream.state == stateOpen
|
||||
if rp.method == "HEAD" && bodyOpen {
|
||||
bodyOpen := !f.StreamEnded()
|
||||
if method == "HEAD" && bodyOpen {
|
||||
// HEAD requests can't have bodies
|
||||
return nil, nil, StreamError{rp.stream.id, ErrCodeProtocol}
|
||||
return nil, nil, StreamError{f.StreamID, ErrCodeProtocol}
|
||||
}
|
||||
var tlsState *tls.ConnectionState // nil if not scheme https
|
||||
|
||||
if rp.scheme == "https" {
|
||||
if scheme == "https" {
|
||||
tlsState = sc.tlsState
|
||||
}
|
||||
authority := rp.authority
|
||||
if authority == "" {
|
||||
authority = rp.header.Get("Host")
|
||||
|
||||
header := make(http.Header)
|
||||
for _, hf := range f.RegularFields() {
|
||||
header.Add(sc.canonicalHeader(hf.Name), hf.Value)
|
||||
}
|
||||
needsContinue := rp.header.Get("Expect") == "100-continue"
|
||||
|
||||
if authority == "" {
|
||||
authority = header.Get("Host")
|
||||
}
|
||||
needsContinue := header.Get("Expect") == "100-continue"
|
||||
if needsContinue {
|
||||
rp.header.Del("Expect")
|
||||
header.Del("Expect")
|
||||
}
|
||||
// Merge Cookie headers into one "; "-delimited value.
|
||||
if cookies := rp.header["Cookie"]; len(cookies) > 1 {
|
||||
rp.header.Set("Cookie", strings.Join(cookies, "; "))
|
||||
if cookies := header["Cookie"]; len(cookies) > 1 {
|
||||
header.Set("Cookie", strings.Join(cookies, "; "))
|
||||
}
|
||||
|
||||
// Setup Trailers
|
||||
var trailer http.Header
|
||||
for _, v := range rp.header["Trailer"] {
|
||||
for _, v := range header["Trailer"] {
|
||||
for _, key := range strings.Split(v, ",") {
|
||||
key = http.CanonicalHeaderKey(strings.TrimSpace(key))
|
||||
switch key {
|
||||
|
|
@ -1726,31 +1594,31 @@ func (sc *serverConn) newWriterAndRequest() (*responseWriter, *http.Request, err
|
|||
}
|
||||
}
|
||||
}
|
||||
delete(rp.header, "Trailer")
|
||||
delete(header, "Trailer")
|
||||
|
||||
body := &requestBody{
|
||||
conn: sc,
|
||||
stream: rp.stream,
|
||||
stream: st,
|
||||
needsContinue: needsContinue,
|
||||
}
|
||||
var url_ *url.URL
|
||||
var requestURI string
|
||||
if isConnect {
|
||||
url_ = &url.URL{Host: rp.authority}
|
||||
requestURI = rp.authority // mimic HTTP/1 server behavior
|
||||
url_ = &url.URL{Host: authority}
|
||||
requestURI = authority // mimic HTTP/1 server behavior
|
||||
} else {
|
||||
var err error
|
||||
url_, err = url.ParseRequestURI(rp.path)
|
||||
url_, err = url.ParseRequestURI(path)
|
||||
if err != nil {
|
||||
return nil, nil, StreamError{rp.stream.id, ErrCodeProtocol}
|
||||
return nil, nil, StreamError{f.StreamID, ErrCodeProtocol}
|
||||
}
|
||||
requestURI = rp.path
|
||||
requestURI = path
|
||||
}
|
||||
req := &http.Request{
|
||||
Method: rp.method,
|
||||
Method: method,
|
||||
URL: url_,
|
||||
RemoteAddr: sc.remoteAddrStr,
|
||||
Header: rp.header,
|
||||
Header: header,
|
||||
RequestURI: requestURI,
|
||||
Proto: "HTTP/2.0",
|
||||
ProtoMajor: 2,
|
||||
|
|
@ -1760,12 +1628,18 @@ func (sc *serverConn) newWriterAndRequest() (*responseWriter, *http.Request, err
|
|||
Body: body,
|
||||
Trailer: trailer,
|
||||
}
|
||||
req = requestWithContext(req, st.ctx)
|
||||
if bodyOpen {
|
||||
// Disabled, per golang.org/issue/14960:
|
||||
// st.reqBuf = sc.getRequestBodyBuf()
|
||||
// TODO: remove this 64k of garbage per request (again, but without a data race):
|
||||
buf := make([]byte, initialWindowSize)
|
||||
|
||||
body.pipe = &pipe{
|
||||
b: &fixedBuffer{buf: make([]byte, initialWindowSize)}, // TODO: garbage
|
||||
b: &fixedBuffer{buf: buf},
|
||||
}
|
||||
|
||||
if vv, ok := rp.header["Content-Length"]; ok {
|
||||
if vv, ok := header["Content-Length"]; ok {
|
||||
req.ContentLength, _ = strconv.ParseInt(vv[0], 10, 64)
|
||||
} else {
|
||||
req.ContentLength = -1
|
||||
|
|
@ -1778,7 +1652,7 @@ func (sc *serverConn) newWriterAndRequest() (*responseWriter, *http.Request, err
|
|||
rws.conn = sc
|
||||
rws.bw = bwSave
|
||||
rws.bw.Reset(chunkWriter{rws})
|
||||
rws.stream = rp.stream
|
||||
rws.stream = st
|
||||
rws.req = req
|
||||
rws.body = body
|
||||
|
||||
|
|
@ -1786,10 +1660,20 @@ func (sc *serverConn) newWriterAndRequest() (*responseWriter, *http.Request, err
|
|||
return rw, req, nil
|
||||
}
|
||||
|
||||
func (sc *serverConn) getRequestBodyBuf() []byte {
|
||||
sc.serveG.check()
|
||||
if buf := sc.freeRequestBodyBuf; buf != nil {
|
||||
sc.freeRequestBodyBuf = nil
|
||||
return buf
|
||||
}
|
||||
return make([]byte, initialWindowSize)
|
||||
}
|
||||
|
||||
// Run on its own goroutine.
|
||||
func (sc *serverConn) runHandler(rw *responseWriter, req *http.Request, handler func(http.ResponseWriter, *http.Request)) {
|
||||
didPanic := true
|
||||
defer func() {
|
||||
rw.rws.stream.cancelCtx()
|
||||
if didPanic {
|
||||
e := recover()
|
||||
// Same as net/http:
|
||||
|
|
@ -1942,7 +1826,7 @@ type requestBody struct {
|
|||
|
||||
func (b *requestBody) Close() error {
|
||||
if b.pipe != nil {
|
||||
b.pipe.CloseWithError(errClosedBody)
|
||||
b.pipe.BreakWithError(errClosedBody)
|
||||
}
|
||||
b.closed = true
|
||||
return nil
|
||||
|
|
@ -2017,9 +1901,9 @@ func (rws *responseWriterState) hasTrailers() bool { return len(rws.trailers) !=
|
|||
// written in the trailers at the end of the response.
|
||||
func (rws *responseWriterState) declareTrailer(k string) {
|
||||
k = http.CanonicalHeaderKey(k)
|
||||
switch k {
|
||||
case "Transfer-Encoding", "Content-Length", "Trailer":
|
||||
if !ValidTrailerHeader(k) {
|
||||
// Forbidden by RFC 2616 14.40.
|
||||
rws.conn.logf("ignoring invalid trailer %q", k)
|
||||
return
|
||||
}
|
||||
if !strSliceContains(rws.trailers, k) {
|
||||
|
|
@ -2160,7 +2044,12 @@ func (rws *responseWriterState) promoteUndeclaredTrailers() {
|
|||
rws.declareTrailer(trailerKey)
|
||||
rws.handlerHeader[http.CanonicalHeaderKey(trailerKey)] = vv
|
||||
}
|
||||
sort.Strings(rws.trailers)
|
||||
|
||||
if len(rws.trailers) > 1 {
|
||||
sorter := sorterPool.Get().(*sorter)
|
||||
sorter.SortStrings(rws.trailers)
|
||||
sorterPool.Put(sorter)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *responseWriter) Flush() {
|
||||
|
|
@ -2306,3 +2195,69 @@ func foreachHeaderElement(v string, fn func(string)) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// From http://httpwg.org/specs/rfc7540.html#rfc.section.8.1.2.2
|
||||
var connHeaders = []string{
|
||||
"Connection",
|
||||
"Keep-Alive",
|
||||
"Proxy-Connection",
|
||||
"Transfer-Encoding",
|
||||
"Upgrade",
|
||||
}
|
||||
|
||||
// checkValidHTTP2Request checks whether req is a valid HTTP/2 request,
|
||||
// per RFC 7540 Section 8.1.2.2.
|
||||
// The returned error is reported to users.
|
||||
func checkValidHTTP2Request(req *http.Request) error {
|
||||
for _, h := range connHeaders {
|
||||
if _, ok := req.Header[h]; ok {
|
||||
return fmt.Errorf("request header %q is not valid in HTTP/2", h)
|
||||
}
|
||||
}
|
||||
te := req.Header["Te"]
|
||||
if len(te) > 0 && (len(te) > 1 || (te[0] != "trailers" && te[0] != "")) {
|
||||
return errors.New(`request header "TE" may only be "trailers" in HTTP/2`)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func new400Handler(err error) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
// ValidTrailerHeader reports whether name is a valid header field name to appear
|
||||
// in trailers.
|
||||
// See: http://tools.ietf.org/html/rfc7230#section-4.1.2
|
||||
func ValidTrailerHeader(name string) bool {
|
||||
name = http.CanonicalHeaderKey(name)
|
||||
if strings.HasPrefix(name, "If-") || badTrailer[name] {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
var badTrailer = map[string]bool{
|
||||
"Authorization": true,
|
||||
"Cache-Control": true,
|
||||
"Connection": true,
|
||||
"Content-Encoding": true,
|
||||
"Content-Length": true,
|
||||
"Content-Range": true,
|
||||
"Content-Type": true,
|
||||
"Expect": true,
|
||||
"Host": true,
|
||||
"Keep-Alive": true,
|
||||
"Max-Forwards": true,
|
||||
"Pragma": true,
|
||||
"Proxy-Authenticate": true,
|
||||
"Proxy-Authorization": true,
|
||||
"Proxy-Connection": true,
|
||||
"Range": true,
|
||||
"Realm": true,
|
||||
"Te": true,
|
||||
"Trailer": true,
|
||||
"Transfer-Encoding": true,
|
||||
"Www-Authenticate": true,
|
||||
}
|
||||
|
|
|
|||
704
vendor/golang.org/x/net/http2/transport.go
generated
vendored
704
vendor/golang.org/x/net/http2/transport.go
generated
vendored
File diff suppressed because it is too large
Load diff
23
vendor/golang.org/x/net/http2/write.go
generated
vendored
23
vendor/golang.org/x/net/http2/write.go
generated
vendored
|
|
@ -9,10 +9,10 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/http2/hpack"
|
||||
"golang.org/x/net/lex/httplex"
|
||||
)
|
||||
|
||||
// writeFramer is implemented by any type that is used to write frames.
|
||||
|
|
@ -230,25 +230,26 @@ func (wu writeWindowUpdate) writeFrame(ctx writeContext) error {
|
|||
}
|
||||
|
||||
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)
|
||||
sorter := sorterPool.Get().(*sorter)
|
||||
// Using defer here, since the returned keys from the
|
||||
// sorter.Keys method is only valid until the sorter
|
||||
// is returned:
|
||||
defer sorterPool.Put(sorter)
|
||||
keys = sorter.Keys(h)
|
||||
}
|
||||
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.
|
||||
if !validWireHeaderFieldName(k) {
|
||||
// Skip it as backup paranoia. Per
|
||||
// golang.org/issue/14048, these should
|
||||
// already be rejected at a higher level.
|
||||
continue
|
||||
}
|
||||
isTE := k == "transfer-encoding"
|
||||
for _, v := range vv {
|
||||
if !validHeaderFieldValue(v) {
|
||||
if !httplex.ValidHeaderFieldValue(v) {
|
||||
// TODO: return an error? golang.org/issue/14048
|
||||
// For now just omit it.
|
||||
continue
|
||||
|
|
|
|||
525
vendor/golang.org/x/net/internal/timeseries/timeseries.go
generated
vendored
Normal file
525
vendor/golang.org/x/net/internal/timeseries/timeseries.go
generated
vendored
Normal file
|
|
@ -0,0 +1,525 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package timeseries implements a time series structure for stats collection.
|
||||
package timeseries
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
timeSeriesNumBuckets = 64
|
||||
minuteHourSeriesNumBuckets = 60
|
||||
)
|
||||
|
||||
var timeSeriesResolutions = []time.Duration{
|
||||
1 * time.Second,
|
||||
10 * time.Second,
|
||||
1 * time.Minute,
|
||||
10 * time.Minute,
|
||||
1 * time.Hour,
|
||||
6 * time.Hour,
|
||||
24 * time.Hour, // 1 day
|
||||
7 * 24 * time.Hour, // 1 week
|
||||
4 * 7 * 24 * time.Hour, // 4 weeks
|
||||
16 * 7 * 24 * time.Hour, // 16 weeks
|
||||
}
|
||||
|
||||
var minuteHourSeriesResolutions = []time.Duration{
|
||||
1 * time.Second,
|
||||
1 * time.Minute,
|
||||
}
|
||||
|
||||
// An Observable is a kind of data that can be aggregated in a time series.
|
||||
type Observable interface {
|
||||
Multiply(ratio float64) // Multiplies the data in self by a given ratio
|
||||
Add(other Observable) // Adds the data from a different observation to self
|
||||
Clear() // Clears the observation so it can be reused.
|
||||
CopyFrom(other Observable) // Copies the contents of a given observation to self
|
||||
}
|
||||
|
||||
// Float attaches the methods of Observable to a float64.
|
||||
type Float float64
|
||||
|
||||
// NewFloat returns a Float.
|
||||
func NewFloat() Observable {
|
||||
f := Float(0)
|
||||
return &f
|
||||
}
|
||||
|
||||
// String returns the float as a string.
|
||||
func (f *Float) String() string { return fmt.Sprintf("%g", f.Value()) }
|
||||
|
||||
// Value returns the float's value.
|
||||
func (f *Float) Value() float64 { return float64(*f) }
|
||||
|
||||
func (f *Float) Multiply(ratio float64) { *f *= Float(ratio) }
|
||||
|
||||
func (f *Float) Add(other Observable) {
|
||||
o := other.(*Float)
|
||||
*f += *o
|
||||
}
|
||||
|
||||
func (f *Float) Clear() { *f = 0 }
|
||||
|
||||
func (f *Float) CopyFrom(other Observable) {
|
||||
o := other.(*Float)
|
||||
*f = *o
|
||||
}
|
||||
|
||||
// A Clock tells the current time.
|
||||
type Clock interface {
|
||||
Time() time.Time
|
||||
}
|
||||
|
||||
type defaultClock int
|
||||
|
||||
var defaultClockInstance defaultClock
|
||||
|
||||
func (defaultClock) Time() time.Time { return time.Now() }
|
||||
|
||||
// Information kept per level. Each level consists of a circular list of
|
||||
// observations. The start of the level may be derived from end and the
|
||||
// len(buckets) * sizeInMillis.
|
||||
type tsLevel struct {
|
||||
oldest int // index to oldest bucketed Observable
|
||||
newest int // index to newest bucketed Observable
|
||||
end time.Time // end timestamp for this level
|
||||
size time.Duration // duration of the bucketed Observable
|
||||
buckets []Observable // collections of observations
|
||||
provider func() Observable // used for creating new Observable
|
||||
}
|
||||
|
||||
func (l *tsLevel) Clear() {
|
||||
l.oldest = 0
|
||||
l.newest = len(l.buckets) - 1
|
||||
l.end = time.Time{}
|
||||
for i := range l.buckets {
|
||||
if l.buckets[i] != nil {
|
||||
l.buckets[i].Clear()
|
||||
l.buckets[i] = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *tsLevel) InitLevel(size time.Duration, numBuckets int, f func() Observable) {
|
||||
l.size = size
|
||||
l.provider = f
|
||||
l.buckets = make([]Observable, numBuckets)
|
||||
}
|
||||
|
||||
// Keeps a sequence of levels. Each level is responsible for storing data at
|
||||
// a given resolution. For example, the first level stores data at a one
|
||||
// minute resolution while the second level stores data at a one hour
|
||||
// resolution.
|
||||
|
||||
// Each level is represented by a sequence of buckets. Each bucket spans an
|
||||
// interval equal to the resolution of the level. New observations are added
|
||||
// to the last bucket.
|
||||
type timeSeries struct {
|
||||
provider func() Observable // make more Observable
|
||||
numBuckets int // number of buckets in each level
|
||||
levels []*tsLevel // levels of bucketed Observable
|
||||
lastAdd time.Time // time of last Observable tracked
|
||||
total Observable // convenient aggregation of all Observable
|
||||
clock Clock // Clock for getting current time
|
||||
pending Observable // observations not yet bucketed
|
||||
pendingTime time.Time // what time are we keeping in pending
|
||||
dirty bool // if there are pending observations
|
||||
}
|
||||
|
||||
// init initializes a level according to the supplied criteria.
|
||||
func (ts *timeSeries) init(resolutions []time.Duration, f func() Observable, numBuckets int, clock Clock) {
|
||||
ts.provider = f
|
||||
ts.numBuckets = numBuckets
|
||||
ts.clock = clock
|
||||
ts.levels = make([]*tsLevel, len(resolutions))
|
||||
|
||||
for i := range resolutions {
|
||||
if i > 0 && resolutions[i-1] >= resolutions[i] {
|
||||
log.Print("timeseries: resolutions must be monotonically increasing")
|
||||
break
|
||||
}
|
||||
newLevel := new(tsLevel)
|
||||
newLevel.InitLevel(resolutions[i], ts.numBuckets, ts.provider)
|
||||
ts.levels[i] = newLevel
|
||||
}
|
||||
|
||||
ts.Clear()
|
||||
}
|
||||
|
||||
// Clear removes all observations from the time series.
|
||||
func (ts *timeSeries) Clear() {
|
||||
ts.lastAdd = time.Time{}
|
||||
ts.total = ts.resetObservation(ts.total)
|
||||
ts.pending = ts.resetObservation(ts.pending)
|
||||
ts.pendingTime = time.Time{}
|
||||
ts.dirty = false
|
||||
|
||||
for i := range ts.levels {
|
||||
ts.levels[i].Clear()
|
||||
}
|
||||
}
|
||||
|
||||
// Add records an observation at the current time.
|
||||
func (ts *timeSeries) Add(observation Observable) {
|
||||
ts.AddWithTime(observation, ts.clock.Time())
|
||||
}
|
||||
|
||||
// AddWithTime records an observation at the specified time.
|
||||
func (ts *timeSeries) AddWithTime(observation Observable, t time.Time) {
|
||||
|
||||
smallBucketDuration := ts.levels[0].size
|
||||
|
||||
if t.After(ts.lastAdd) {
|
||||
ts.lastAdd = t
|
||||
}
|
||||
|
||||
if t.After(ts.pendingTime) {
|
||||
ts.advance(t)
|
||||
ts.mergePendingUpdates()
|
||||
ts.pendingTime = ts.levels[0].end
|
||||
ts.pending.CopyFrom(observation)
|
||||
ts.dirty = true
|
||||
} else if t.After(ts.pendingTime.Add(-1 * smallBucketDuration)) {
|
||||
// The observation is close enough to go into the pending bucket.
|
||||
// This compensates for clock skewing and small scheduling delays
|
||||
// by letting the update stay in the fast path.
|
||||
ts.pending.Add(observation)
|
||||
ts.dirty = true
|
||||
} else {
|
||||
ts.mergeValue(observation, t)
|
||||
}
|
||||
}
|
||||
|
||||
// mergeValue inserts the observation at the specified time in the past into all levels.
|
||||
func (ts *timeSeries) mergeValue(observation Observable, t time.Time) {
|
||||
for _, level := range ts.levels {
|
||||
index := (ts.numBuckets - 1) - int(level.end.Sub(t)/level.size)
|
||||
if 0 <= index && index < ts.numBuckets {
|
||||
bucketNumber := (level.oldest + index) % ts.numBuckets
|
||||
if level.buckets[bucketNumber] == nil {
|
||||
level.buckets[bucketNumber] = level.provider()
|
||||
}
|
||||
level.buckets[bucketNumber].Add(observation)
|
||||
}
|
||||
}
|
||||
ts.total.Add(observation)
|
||||
}
|
||||
|
||||
// mergePendingUpdates applies the pending updates into all levels.
|
||||
func (ts *timeSeries) mergePendingUpdates() {
|
||||
if ts.dirty {
|
||||
ts.mergeValue(ts.pending, ts.pendingTime)
|
||||
ts.pending = ts.resetObservation(ts.pending)
|
||||
ts.dirty = false
|
||||
}
|
||||
}
|
||||
|
||||
// advance cycles the buckets at each level until the latest bucket in
|
||||
// each level can hold the time specified.
|
||||
func (ts *timeSeries) advance(t time.Time) {
|
||||
if !t.After(ts.levels[0].end) {
|
||||
return
|
||||
}
|
||||
for i := 0; i < len(ts.levels); i++ {
|
||||
level := ts.levels[i]
|
||||
if !level.end.Before(t) {
|
||||
break
|
||||
}
|
||||
|
||||
// If the time is sufficiently far, just clear the level and advance
|
||||
// directly.
|
||||
if !t.Before(level.end.Add(level.size * time.Duration(ts.numBuckets))) {
|
||||
for _, b := range level.buckets {
|
||||
ts.resetObservation(b)
|
||||
}
|
||||
level.end = time.Unix(0, (t.UnixNano()/level.size.Nanoseconds())*level.size.Nanoseconds())
|
||||
}
|
||||
|
||||
for t.After(level.end) {
|
||||
level.end = level.end.Add(level.size)
|
||||
level.newest = level.oldest
|
||||
level.oldest = (level.oldest + 1) % ts.numBuckets
|
||||
ts.resetObservation(level.buckets[level.newest])
|
||||
}
|
||||
|
||||
t = level.end
|
||||
}
|
||||
}
|
||||
|
||||
// Latest returns the sum of the num latest buckets from the level.
|
||||
func (ts *timeSeries) Latest(level, num int) Observable {
|
||||
now := ts.clock.Time()
|
||||
if ts.levels[0].end.Before(now) {
|
||||
ts.advance(now)
|
||||
}
|
||||
|
||||
ts.mergePendingUpdates()
|
||||
|
||||
result := ts.provider()
|
||||
l := ts.levels[level]
|
||||
index := l.newest
|
||||
|
||||
for i := 0; i < num; i++ {
|
||||
if l.buckets[index] != nil {
|
||||
result.Add(l.buckets[index])
|
||||
}
|
||||
if index == 0 {
|
||||
index = ts.numBuckets
|
||||
}
|
||||
index--
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// LatestBuckets returns a copy of the num latest buckets from level.
|
||||
func (ts *timeSeries) LatestBuckets(level, num int) []Observable {
|
||||
if level < 0 || level > len(ts.levels) {
|
||||
log.Print("timeseries: bad level argument: ", level)
|
||||
return nil
|
||||
}
|
||||
if num < 0 || num >= ts.numBuckets {
|
||||
log.Print("timeseries: bad num argument: ", num)
|
||||
return nil
|
||||
}
|
||||
|
||||
results := make([]Observable, num)
|
||||
now := ts.clock.Time()
|
||||
if ts.levels[0].end.Before(now) {
|
||||
ts.advance(now)
|
||||
}
|
||||
|
||||
ts.mergePendingUpdates()
|
||||
|
||||
l := ts.levels[level]
|
||||
index := l.newest
|
||||
|
||||
for i := 0; i < num; i++ {
|
||||
result := ts.provider()
|
||||
results[i] = result
|
||||
if l.buckets[index] != nil {
|
||||
result.CopyFrom(l.buckets[index])
|
||||
}
|
||||
|
||||
if index == 0 {
|
||||
index = ts.numBuckets
|
||||
}
|
||||
index -= 1
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
// ScaleBy updates observations by scaling by factor.
|
||||
func (ts *timeSeries) ScaleBy(factor float64) {
|
||||
for _, l := range ts.levels {
|
||||
for i := 0; i < ts.numBuckets; i++ {
|
||||
l.buckets[i].Multiply(factor)
|
||||
}
|
||||
}
|
||||
|
||||
ts.total.Multiply(factor)
|
||||
ts.pending.Multiply(factor)
|
||||
}
|
||||
|
||||
// Range returns the sum of observations added over the specified time range.
|
||||
// If start or finish times don't fall on bucket boundaries of the same
|
||||
// level, then return values are approximate answers.
|
||||
func (ts *timeSeries) Range(start, finish time.Time) Observable {
|
||||
return ts.ComputeRange(start, finish, 1)[0]
|
||||
}
|
||||
|
||||
// Recent returns the sum of observations from the last delta.
|
||||
func (ts *timeSeries) Recent(delta time.Duration) Observable {
|
||||
now := ts.clock.Time()
|
||||
return ts.Range(now.Add(-delta), now)
|
||||
}
|
||||
|
||||
// Total returns the total of all observations.
|
||||
func (ts *timeSeries) Total() Observable {
|
||||
ts.mergePendingUpdates()
|
||||
return ts.total
|
||||
}
|
||||
|
||||
// ComputeRange computes a specified number of values into a slice using
|
||||
// the observations recorded over the specified time period. The return
|
||||
// values are approximate if the start or finish times don't fall on the
|
||||
// bucket boundaries at the same level or if the number of buckets spanning
|
||||
// the range is not an integral multiple of num.
|
||||
func (ts *timeSeries) ComputeRange(start, finish time.Time, num int) []Observable {
|
||||
if start.After(finish) {
|
||||
log.Printf("timeseries: start > finish, %v>%v", start, finish)
|
||||
return nil
|
||||
}
|
||||
|
||||
if num < 0 {
|
||||
log.Printf("timeseries: num < 0, %v", num)
|
||||
return nil
|
||||
}
|
||||
|
||||
results := make([]Observable, num)
|
||||
|
||||
for _, l := range ts.levels {
|
||||
if !start.Before(l.end.Add(-l.size * time.Duration(ts.numBuckets))) {
|
||||
ts.extract(l, start, finish, num, results)
|
||||
return results
|
||||
}
|
||||
}
|
||||
|
||||
// Failed to find a level that covers the desired range. So just
|
||||
// extract from the last level, even if it doesn't cover the entire
|
||||
// desired range.
|
||||
ts.extract(ts.levels[len(ts.levels)-1], start, finish, num, results)
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
// RecentList returns the specified number of values in slice over the most
|
||||
// recent time period of the specified range.
|
||||
func (ts *timeSeries) RecentList(delta time.Duration, num int) []Observable {
|
||||
if delta < 0 {
|
||||
return nil
|
||||
}
|
||||
now := ts.clock.Time()
|
||||
return ts.ComputeRange(now.Add(-delta), now, num)
|
||||
}
|
||||
|
||||
// extract returns a slice of specified number of observations from a given
|
||||
// level over a given range.
|
||||
func (ts *timeSeries) extract(l *tsLevel, start, finish time.Time, num int, results []Observable) {
|
||||
ts.mergePendingUpdates()
|
||||
|
||||
srcInterval := l.size
|
||||
dstInterval := finish.Sub(start) / time.Duration(num)
|
||||
dstStart := start
|
||||
srcStart := l.end.Add(-srcInterval * time.Duration(ts.numBuckets))
|
||||
|
||||
srcIndex := 0
|
||||
|
||||
// Where should scanning start?
|
||||
if dstStart.After(srcStart) {
|
||||
advance := dstStart.Sub(srcStart) / srcInterval
|
||||
srcIndex += int(advance)
|
||||
srcStart = srcStart.Add(advance * srcInterval)
|
||||
}
|
||||
|
||||
// The i'th value is computed as show below.
|
||||
// interval = (finish/start)/num
|
||||
// i'th value = sum of observation in range
|
||||
// [ start + i * interval,
|
||||
// start + (i + 1) * interval )
|
||||
for i := 0; i < num; i++ {
|
||||
results[i] = ts.resetObservation(results[i])
|
||||
dstEnd := dstStart.Add(dstInterval)
|
||||
for srcIndex < ts.numBuckets && srcStart.Before(dstEnd) {
|
||||
srcEnd := srcStart.Add(srcInterval)
|
||||
if srcEnd.After(ts.lastAdd) {
|
||||
srcEnd = ts.lastAdd
|
||||
}
|
||||
|
||||
if !srcEnd.Before(dstStart) {
|
||||
srcValue := l.buckets[(srcIndex+l.oldest)%ts.numBuckets]
|
||||
if !srcStart.Before(dstStart) && !srcEnd.After(dstEnd) {
|
||||
// dst completely contains src.
|
||||
if srcValue != nil {
|
||||
results[i].Add(srcValue)
|
||||
}
|
||||
} else {
|
||||
// dst partially overlaps src.
|
||||
overlapStart := maxTime(srcStart, dstStart)
|
||||
overlapEnd := minTime(srcEnd, dstEnd)
|
||||
base := srcEnd.Sub(srcStart)
|
||||
fraction := overlapEnd.Sub(overlapStart).Seconds() / base.Seconds()
|
||||
|
||||
used := ts.provider()
|
||||
if srcValue != nil {
|
||||
used.CopyFrom(srcValue)
|
||||
}
|
||||
used.Multiply(fraction)
|
||||
results[i].Add(used)
|
||||
}
|
||||
|
||||
if srcEnd.After(dstEnd) {
|
||||
break
|
||||
}
|
||||
}
|
||||
srcIndex++
|
||||
srcStart = srcStart.Add(srcInterval)
|
||||
}
|
||||
dstStart = dstStart.Add(dstInterval)
|
||||
}
|
||||
}
|
||||
|
||||
// resetObservation clears the content so the struct may be reused.
|
||||
func (ts *timeSeries) resetObservation(observation Observable) Observable {
|
||||
if observation == nil {
|
||||
observation = ts.provider()
|
||||
} else {
|
||||
observation.Clear()
|
||||
}
|
||||
return observation
|
||||
}
|
||||
|
||||
// TimeSeries tracks data at granularities from 1 second to 16 weeks.
|
||||
type TimeSeries struct {
|
||||
timeSeries
|
||||
}
|
||||
|
||||
// NewTimeSeries creates a new TimeSeries using the function provided for creating new Observable.
|
||||
func NewTimeSeries(f func() Observable) *TimeSeries {
|
||||
return NewTimeSeriesWithClock(f, defaultClockInstance)
|
||||
}
|
||||
|
||||
// NewTimeSeriesWithClock creates a new TimeSeries using the function provided for creating new Observable and the clock for
|
||||
// assigning timestamps.
|
||||
func NewTimeSeriesWithClock(f func() Observable, clock Clock) *TimeSeries {
|
||||
ts := new(TimeSeries)
|
||||
ts.timeSeries.init(timeSeriesResolutions, f, timeSeriesNumBuckets, clock)
|
||||
return ts
|
||||
}
|
||||
|
||||
// MinuteHourSeries tracks data at granularities of 1 minute and 1 hour.
|
||||
type MinuteHourSeries struct {
|
||||
timeSeries
|
||||
}
|
||||
|
||||
// NewMinuteHourSeries creates a new MinuteHourSeries using the function provided for creating new Observable.
|
||||
func NewMinuteHourSeries(f func() Observable) *MinuteHourSeries {
|
||||
return NewMinuteHourSeriesWithClock(f, defaultClockInstance)
|
||||
}
|
||||
|
||||
// NewMinuteHourSeriesWithClock creates a new MinuteHourSeries using the function provided for creating new Observable and the clock for
|
||||
// assigning timestamps.
|
||||
func NewMinuteHourSeriesWithClock(f func() Observable, clock Clock) *MinuteHourSeries {
|
||||
ts := new(MinuteHourSeries)
|
||||
ts.timeSeries.init(minuteHourSeriesResolutions, f,
|
||||
minuteHourSeriesNumBuckets, clock)
|
||||
return ts
|
||||
}
|
||||
|
||||
func (ts *MinuteHourSeries) Minute() Observable {
|
||||
return ts.timeSeries.Latest(0, 60)
|
||||
}
|
||||
|
||||
func (ts *MinuteHourSeries) Hour() Observable {
|
||||
return ts.timeSeries.Latest(1, 60)
|
||||
}
|
||||
|
||||
func minTime(a, b time.Time) time.Time {
|
||||
if a.Before(b) {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func maxTime(a, b time.Time) time.Time {
|
||||
if a.After(b) {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
312
vendor/golang.org/x/net/lex/httplex/httplex.go
generated
vendored
Normal file
312
vendor/golang.org/x/net/lex/httplex/httplex.go
generated
vendored
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package httplex contains rules around lexical matters of various
|
||||
// HTTP-related specifications.
|
||||
//
|
||||
// This package is shared by the standard library (which vendors it)
|
||||
// and x/net/http2. It comes with no API stability promise.
|
||||
package httplex
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
func IsTokenRune(r rune) bool {
|
||||
i := int(r)
|
||||
return i < len(isTokenTable) && isTokenTable[i]
|
||||
}
|
||||
|
||||
func isNotToken(r rune) bool {
|
||||
return !IsTokenRune(r)
|
||||
}
|
||||
|
||||
// HeaderValuesContainsToken reports whether any string in values
|
||||
// contains the provided token, ASCII case-insensitively.
|
||||
func HeaderValuesContainsToken(values []string, token string) bool {
|
||||
for _, v := range values {
|
||||
if headerValueContainsToken(v, token) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isOWS reports whether b is an optional whitespace byte, as defined
|
||||
// by RFC 7230 section 3.2.3.
|
||||
func isOWS(b byte) bool { return b == ' ' || b == '\t' }
|
||||
|
||||
// trimOWS returns x with all optional whitespace removes from the
|
||||
// beginning and end.
|
||||
func trimOWS(x string) string {
|
||||
// TODO: consider using strings.Trim(x, " \t") instead,
|
||||
// if and when it's fast enough. See issue 10292.
|
||||
// But this ASCII-only code will probably always beat UTF-8
|
||||
// aware code.
|
||||
for len(x) > 0 && isOWS(x[0]) {
|
||||
x = x[1:]
|
||||
}
|
||||
for len(x) > 0 && isOWS(x[len(x)-1]) {
|
||||
x = x[:len(x)-1]
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// headerValueContainsToken reports whether v (assumed to be a
|
||||
// 0#element, in the ABNF extension described in RFC 7230 section 7)
|
||||
// contains token amongst its comma-separated tokens, ASCII
|
||||
// case-insensitively.
|
||||
func headerValueContainsToken(v string, token string) bool {
|
||||
v = trimOWS(v)
|
||||
if comma := strings.IndexByte(v, ','); comma != -1 {
|
||||
return tokenEqual(trimOWS(v[:comma]), token) || headerValueContainsToken(v[comma+1:], token)
|
||||
}
|
||||
return tokenEqual(v, token)
|
||||
}
|
||||
|
||||
// lowerASCII returns the ASCII lowercase version of b.
|
||||
func lowerASCII(b byte) byte {
|
||||
if 'A' <= b && b <= 'Z' {
|
||||
return b + ('a' - 'A')
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// tokenEqual reports whether t1 and t2 are equal, ASCII case-insensitively.
|
||||
func tokenEqual(t1, t2 string) bool {
|
||||
if len(t1) != len(t2) {
|
||||
return false
|
||||
}
|
||||
for i, b := range t1 {
|
||||
if b >= utf8.RuneSelf {
|
||||
// No UTF-8 or non-ASCII allowed in tokens.
|
||||
return false
|
||||
}
|
||||
if lowerASCII(byte(b)) != lowerASCII(t2[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// isLWS reports whether b is linear white space, according
|
||||
// to http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2
|
||||
// LWS = [CRLF] 1*( SP | HT )
|
||||
func isLWS(b byte) bool { return b == ' ' || b == '\t' }
|
||||
|
||||
// isCTL reports whether b is a control byte, according
|
||||
// to http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2
|
||||
// CTL = <any US-ASCII control character
|
||||
// (octets 0 - 31) and DEL (127)>
|
||||
func isCTL(b byte) bool {
|
||||
const del = 0x7f // a CTL
|
||||
return b < ' ' || b == del
|
||||
}
|
||||
|
||||
// ValidHeaderFieldName reports whether v is a valid HTTP/1.x header name.
|
||||
// HTTP/2 imposes the additional restriction that uppercase ASCII
|
||||
// letters are not allowed.
|
||||
//
|
||||
// RFC 7230 says:
|
||||
// header-field = field-name ":" OWS field-value OWS
|
||||
// field-name = token
|
||||
// token = 1*tchar
|
||||
// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." /
|
||||
// "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA
|
||||
func ValidHeaderFieldName(v string) bool {
|
||||
if len(v) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, r := range v {
|
||||
if !IsTokenRune(r) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ValidHostHeader reports whether h is a valid host header.
|
||||
func ValidHostHeader(h string) bool {
|
||||
// The latest spec is actually this:
|
||||
//
|
||||
// http://tools.ietf.org/html/rfc7230#section-5.4
|
||||
// Host = uri-host [ ":" port ]
|
||||
//
|
||||
// Where uri-host is:
|
||||
// http://tools.ietf.org/html/rfc3986#section-3.2.2
|
||||
//
|
||||
// But we're going to be much more lenient for now and just
|
||||
// search for any byte that's not a valid byte in any of those
|
||||
// expressions.
|
||||
for i := 0; i < len(h); i++ {
|
||||
if !validHostByte[h[i]] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// See the validHostHeader comment.
|
||||
var validHostByte = [256]bool{
|
||||
'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, 'v': true, 'w': true, 'x': true,
|
||||
'y': true, 'z': 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, // sub-delims
|
||||
'$': true, // sub-delims
|
||||
'%': true, // pct-encoded (and used in IPv6 zones)
|
||||
'&': true, // sub-delims
|
||||
'(': true, // sub-delims
|
||||
')': true, // sub-delims
|
||||
'*': true, // sub-delims
|
||||
'+': true, // sub-delims
|
||||
',': true, // sub-delims
|
||||
'-': true, // unreserved
|
||||
'.': true, // unreserved
|
||||
':': true, // IPv6address + Host expression's optional port
|
||||
';': true, // sub-delims
|
||||
'=': true, // sub-delims
|
||||
'[': true,
|
||||
'\'': true, // sub-delims
|
||||
']': true,
|
||||
'_': true, // unreserved
|
||||
'~': true, // unreserved
|
||||
}
|
||||
|
||||
// ValidHeaderFieldValue reports whether v is a valid "field-value" according to
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 :
|
||||
//
|
||||
// message-header = field-name ":" [ field-value ]
|
||||
// field-value = *( field-content | LWS )
|
||||
// field-content = <the OCTETs making up the field-value
|
||||
// and consisting of either *TEXT or combinations
|
||||
// of token, separators, and quoted-string>
|
||||
//
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2 :
|
||||
//
|
||||
// TEXT = <any OCTET except CTLs,
|
||||
// but including LWS>
|
||||
// LWS = [CRLF] 1*( SP | HT )
|
||||
// CTL = <any US-ASCII control character
|
||||
// (octets 0 - 31) and DEL (127)>
|
||||
//
|
||||
// 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++ {
|
||||
b := v[i]
|
||||
if isCTL(b) && !isLWS(b) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
524
vendor/golang.org/x/net/trace/events.go
generated
vendored
Normal file
524
vendor/golang.org/x/net/trace/events.go
generated
vendored
Normal file
|
|
@ -0,0 +1,524 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package trace
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
)
|
||||
|
||||
var eventsTmpl = template.Must(template.New("events").Funcs(template.FuncMap{
|
||||
"elapsed": elapsed,
|
||||
"trimSpace": strings.TrimSpace,
|
||||
}).Parse(eventsHTML))
|
||||
|
||||
const maxEventsPerLog = 100
|
||||
|
||||
type bucket struct {
|
||||
MaxErrAge time.Duration
|
||||
String string
|
||||
}
|
||||
|
||||
var buckets = []bucket{
|
||||
{0, "total"},
|
||||
{10 * time.Second, "errs<10s"},
|
||||
{1 * time.Minute, "errs<1m"},
|
||||
{10 * time.Minute, "errs<10m"},
|
||||
{1 * time.Hour, "errs<1h"},
|
||||
{10 * time.Hour, "errs<10h"},
|
||||
{24000 * time.Hour, "errors"},
|
||||
}
|
||||
|
||||
// RenderEvents renders the HTML page typically served at /debug/events.
|
||||
// It does not do any auth checking; see AuthRequest for the default auth check
|
||||
// used by the handler registered on http.DefaultServeMux.
|
||||
// req may be nil.
|
||||
func RenderEvents(w http.ResponseWriter, req *http.Request, sensitive bool) {
|
||||
now := time.Now()
|
||||
data := &struct {
|
||||
Families []string // family names
|
||||
Buckets []bucket
|
||||
Counts [][]int // eventLog count per family/bucket
|
||||
|
||||
// Set when a bucket has been selected.
|
||||
Family string
|
||||
Bucket int
|
||||
EventLogs eventLogs
|
||||
Expanded bool
|
||||
}{
|
||||
Buckets: buckets,
|
||||
}
|
||||
|
||||
data.Families = make([]string, 0, len(families))
|
||||
famMu.RLock()
|
||||
for name := range families {
|
||||
data.Families = append(data.Families, name)
|
||||
}
|
||||
famMu.RUnlock()
|
||||
sort.Strings(data.Families)
|
||||
|
||||
// Count the number of eventLogs in each family for each error age.
|
||||
data.Counts = make([][]int, len(data.Families))
|
||||
for i, name := range data.Families {
|
||||
// TODO(sameer): move this loop under the family lock.
|
||||
f := getEventFamily(name)
|
||||
data.Counts[i] = make([]int, len(data.Buckets))
|
||||
for j, b := range data.Buckets {
|
||||
data.Counts[i][j] = f.Count(now, b.MaxErrAge)
|
||||
}
|
||||
}
|
||||
|
||||
if req != nil {
|
||||
var ok bool
|
||||
data.Family, data.Bucket, ok = parseEventsArgs(req)
|
||||
if !ok {
|
||||
// No-op
|
||||
} else {
|
||||
data.EventLogs = getEventFamily(data.Family).Copy(now, buckets[data.Bucket].MaxErrAge)
|
||||
}
|
||||
if data.EventLogs != nil {
|
||||
defer data.EventLogs.Free()
|
||||
sort.Sort(data.EventLogs)
|
||||
}
|
||||
if exp, err := strconv.ParseBool(req.FormValue("exp")); err == nil {
|
||||
data.Expanded = exp
|
||||
}
|
||||
}
|
||||
|
||||
famMu.RLock()
|
||||
defer famMu.RUnlock()
|
||||
if err := eventsTmpl.Execute(w, data); err != nil {
|
||||
log.Printf("net/trace: Failed executing template: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func parseEventsArgs(req *http.Request) (fam string, b int, ok bool) {
|
||||
fam, bStr := req.FormValue("fam"), req.FormValue("b")
|
||||
if fam == "" || bStr == "" {
|
||||
return "", 0, false
|
||||
}
|
||||
b, err := strconv.Atoi(bStr)
|
||||
if err != nil || b < 0 || b >= len(buckets) {
|
||||
return "", 0, false
|
||||
}
|
||||
return fam, b, true
|
||||
}
|
||||
|
||||
// An EventLog provides a log of events associated with a specific object.
|
||||
type EventLog interface {
|
||||
// Printf formats its arguments with fmt.Sprintf and adds the
|
||||
// result to the event log.
|
||||
Printf(format string, a ...interface{})
|
||||
|
||||
// Errorf is like Printf, but it marks this event as an error.
|
||||
Errorf(format string, a ...interface{})
|
||||
|
||||
// Finish declares that this event log is complete.
|
||||
// The event log should not be used after calling this method.
|
||||
Finish()
|
||||
}
|
||||
|
||||
// NewEventLog returns a new EventLog with the specified family name
|
||||
// and title.
|
||||
func NewEventLog(family, title string) EventLog {
|
||||
el := newEventLog()
|
||||
el.ref()
|
||||
el.Family, el.Title = family, title
|
||||
el.Start = time.Now()
|
||||
el.events = make([]logEntry, 0, maxEventsPerLog)
|
||||
el.stack = make([]uintptr, 32)
|
||||
n := runtime.Callers(2, el.stack)
|
||||
el.stack = el.stack[:n]
|
||||
|
||||
getEventFamily(family).add(el)
|
||||
return el
|
||||
}
|
||||
|
||||
func (el *eventLog) Finish() {
|
||||
getEventFamily(el.Family).remove(el)
|
||||
el.unref() // matches ref in New
|
||||
}
|
||||
|
||||
var (
|
||||
famMu sync.RWMutex
|
||||
families = make(map[string]*eventFamily) // family name => family
|
||||
)
|
||||
|
||||
func getEventFamily(fam string) *eventFamily {
|
||||
famMu.Lock()
|
||||
defer famMu.Unlock()
|
||||
f := families[fam]
|
||||
if f == nil {
|
||||
f = &eventFamily{}
|
||||
families[fam] = f
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
type eventFamily struct {
|
||||
mu sync.RWMutex
|
||||
eventLogs eventLogs
|
||||
}
|
||||
|
||||
func (f *eventFamily) add(el *eventLog) {
|
||||
f.mu.Lock()
|
||||
f.eventLogs = append(f.eventLogs, el)
|
||||
f.mu.Unlock()
|
||||
}
|
||||
|
||||
func (f *eventFamily) remove(el *eventLog) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
for i, el0 := range f.eventLogs {
|
||||
if el == el0 {
|
||||
copy(f.eventLogs[i:], f.eventLogs[i+1:])
|
||||
f.eventLogs = f.eventLogs[:len(f.eventLogs)-1]
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *eventFamily) Count(now time.Time, maxErrAge time.Duration) (n int) {
|
||||
f.mu.RLock()
|
||||
defer f.mu.RUnlock()
|
||||
for _, el := range f.eventLogs {
|
||||
if el.hasRecentError(now, maxErrAge) {
|
||||
n++
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (f *eventFamily) Copy(now time.Time, maxErrAge time.Duration) (els eventLogs) {
|
||||
f.mu.RLock()
|
||||
defer f.mu.RUnlock()
|
||||
els = make(eventLogs, 0, len(f.eventLogs))
|
||||
for _, el := range f.eventLogs {
|
||||
if el.hasRecentError(now, maxErrAge) {
|
||||
el.ref()
|
||||
els = append(els, el)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type eventLogs []*eventLog
|
||||
|
||||
// Free calls unref on each element of the list.
|
||||
func (els eventLogs) Free() {
|
||||
for _, el := range els {
|
||||
el.unref()
|
||||
}
|
||||
}
|
||||
|
||||
// eventLogs may be sorted in reverse chronological order.
|
||||
func (els eventLogs) Len() int { return len(els) }
|
||||
func (els eventLogs) Less(i, j int) bool { return els[i].Start.After(els[j].Start) }
|
||||
func (els eventLogs) Swap(i, j int) { els[i], els[j] = els[j], els[i] }
|
||||
|
||||
// A logEntry is a timestamped log entry in an event log.
|
||||
type logEntry struct {
|
||||
When time.Time
|
||||
Elapsed time.Duration // since previous event in log
|
||||
NewDay bool // whether this event is on a different day to the previous event
|
||||
What string
|
||||
IsErr bool
|
||||
}
|
||||
|
||||
// WhenString returns a string representation of the elapsed time of the event.
|
||||
// It will include the date if midnight was crossed.
|
||||
func (e logEntry) WhenString() string {
|
||||
if e.NewDay {
|
||||
return e.When.Format("2006/01/02 15:04:05.000000")
|
||||
}
|
||||
return e.When.Format("15:04:05.000000")
|
||||
}
|
||||
|
||||
// An eventLog represents an active event log.
|
||||
type eventLog struct {
|
||||
// Family is the top-level grouping of event logs to which this belongs.
|
||||
Family string
|
||||
|
||||
// Title is the title of this event log.
|
||||
Title string
|
||||
|
||||
// Timing information.
|
||||
Start time.Time
|
||||
|
||||
// Call stack where this event log was created.
|
||||
stack []uintptr
|
||||
|
||||
// Append-only sequence of events.
|
||||
//
|
||||
// TODO(sameer): change this to a ring buffer to avoid the array copy
|
||||
// when we hit maxEventsPerLog.
|
||||
mu sync.RWMutex
|
||||
events []logEntry
|
||||
LastErrorTime time.Time
|
||||
discarded int
|
||||
|
||||
refs int32 // how many buckets this is in
|
||||
}
|
||||
|
||||
func (el *eventLog) reset() {
|
||||
// Clear all but the mutex. Mutexes may not be copied, even when unlocked.
|
||||
el.Family = ""
|
||||
el.Title = ""
|
||||
el.Start = time.Time{}
|
||||
el.stack = nil
|
||||
el.events = nil
|
||||
el.LastErrorTime = time.Time{}
|
||||
el.discarded = 0
|
||||
el.refs = 0
|
||||
}
|
||||
|
||||
func (el *eventLog) hasRecentError(now time.Time, maxErrAge time.Duration) bool {
|
||||
if maxErrAge == 0 {
|
||||
return true
|
||||
}
|
||||
el.mu.RLock()
|
||||
defer el.mu.RUnlock()
|
||||
return now.Sub(el.LastErrorTime) < maxErrAge
|
||||
}
|
||||
|
||||
// delta returns the elapsed time since the last event or the log start,
|
||||
// and whether it spans midnight.
|
||||
// L >= el.mu
|
||||
func (el *eventLog) delta(t time.Time) (time.Duration, bool) {
|
||||
if len(el.events) == 0 {
|
||||
return t.Sub(el.Start), false
|
||||
}
|
||||
prev := el.events[len(el.events)-1].When
|
||||
return t.Sub(prev), prev.Day() != t.Day()
|
||||
|
||||
}
|
||||
|
||||
func (el *eventLog) Printf(format string, a ...interface{}) {
|
||||
el.printf(false, format, a...)
|
||||
}
|
||||
|
||||
func (el *eventLog) Errorf(format string, a ...interface{}) {
|
||||
el.printf(true, format, a...)
|
||||
}
|
||||
|
||||
func (el *eventLog) printf(isErr bool, format string, a ...interface{}) {
|
||||
e := logEntry{When: time.Now(), IsErr: isErr, What: fmt.Sprintf(format, a...)}
|
||||
el.mu.Lock()
|
||||
e.Elapsed, e.NewDay = el.delta(e.When)
|
||||
if len(el.events) < maxEventsPerLog {
|
||||
el.events = append(el.events, e)
|
||||
} else {
|
||||
// Discard the oldest event.
|
||||
if el.discarded == 0 {
|
||||
// el.discarded starts at two to count for the event it
|
||||
// is replacing, plus the next one that we are about to
|
||||
// drop.
|
||||
el.discarded = 2
|
||||
} else {
|
||||
el.discarded++
|
||||
}
|
||||
// TODO(sameer): if this causes allocations on a critical path,
|
||||
// change eventLog.What to be a fmt.Stringer, as in trace.go.
|
||||
el.events[0].What = fmt.Sprintf("(%d events discarded)", el.discarded)
|
||||
// The timestamp of the discarded meta-event should be
|
||||
// the time of the last event it is representing.
|
||||
el.events[0].When = el.events[1].When
|
||||
copy(el.events[1:], el.events[2:])
|
||||
el.events[maxEventsPerLog-1] = e
|
||||
}
|
||||
if e.IsErr {
|
||||
el.LastErrorTime = e.When
|
||||
}
|
||||
el.mu.Unlock()
|
||||
}
|
||||
|
||||
func (el *eventLog) ref() {
|
||||
atomic.AddInt32(&el.refs, 1)
|
||||
}
|
||||
|
||||
func (el *eventLog) unref() {
|
||||
if atomic.AddInt32(&el.refs, -1) == 0 {
|
||||
freeEventLog(el)
|
||||
}
|
||||
}
|
||||
|
||||
func (el *eventLog) When() string {
|
||||
return el.Start.Format("2006/01/02 15:04:05.000000")
|
||||
}
|
||||
|
||||
func (el *eventLog) ElapsedTime() string {
|
||||
elapsed := time.Since(el.Start)
|
||||
return fmt.Sprintf("%.6f", elapsed.Seconds())
|
||||
}
|
||||
|
||||
func (el *eventLog) Stack() string {
|
||||
buf := new(bytes.Buffer)
|
||||
tw := tabwriter.NewWriter(buf, 1, 8, 1, '\t', 0)
|
||||
printStackRecord(tw, el.stack)
|
||||
tw.Flush()
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// printStackRecord prints the function + source line information
|
||||
// for a single stack trace.
|
||||
// Adapted from runtime/pprof/pprof.go.
|
||||
func printStackRecord(w io.Writer, stk []uintptr) {
|
||||
for _, pc := range stk {
|
||||
f := runtime.FuncForPC(pc)
|
||||
if f == nil {
|
||||
continue
|
||||
}
|
||||
file, line := f.FileLine(pc)
|
||||
name := f.Name()
|
||||
// Hide runtime.goexit and any runtime functions at the beginning.
|
||||
if strings.HasPrefix(name, "runtime.") {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(w, "# %s\t%s:%d\n", name, file, line)
|
||||
}
|
||||
}
|
||||
|
||||
func (el *eventLog) Events() []logEntry {
|
||||
el.mu.RLock()
|
||||
defer el.mu.RUnlock()
|
||||
return el.events
|
||||
}
|
||||
|
||||
// freeEventLogs is a freelist of *eventLog
|
||||
var freeEventLogs = make(chan *eventLog, 1000)
|
||||
|
||||
// newEventLog returns a event log ready to use.
|
||||
func newEventLog() *eventLog {
|
||||
select {
|
||||
case el := <-freeEventLogs:
|
||||
return el
|
||||
default:
|
||||
return new(eventLog)
|
||||
}
|
||||
}
|
||||
|
||||
// freeEventLog adds el to freeEventLogs if there's room.
|
||||
// This is non-blocking.
|
||||
func freeEventLog(el *eventLog) {
|
||||
el.reset()
|
||||
select {
|
||||
case freeEventLogs <- el:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
const eventsHTML = `
|
||||
<html>
|
||||
<head>
|
||||
<title>events</title>
|
||||
</head>
|
||||
<style type="text/css">
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
table#req-status td.family {
|
||||
padding-right: 2em;
|
||||
}
|
||||
table#req-status td.active {
|
||||
padding-right: 1em;
|
||||
}
|
||||
table#req-status td.empty {
|
||||
color: #aaa;
|
||||
}
|
||||
table#reqs {
|
||||
margin-top: 1em;
|
||||
}
|
||||
table#reqs tr.first {
|
||||
{{if $.Expanded}}font-weight: bold;{{end}}
|
||||
}
|
||||
table#reqs td {
|
||||
font-family: monospace;
|
||||
}
|
||||
table#reqs td.when {
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
table#reqs td.elapsed {
|
||||
padding: 0 0.5em;
|
||||
text-align: right;
|
||||
white-space: pre;
|
||||
width: 10em;
|
||||
}
|
||||
address {
|
||||
font-size: smaller;
|
||||
margin-top: 5em;
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
|
||||
<h1>/debug/events</h1>
|
||||
|
||||
<table id="req-status">
|
||||
{{range $i, $fam := .Families}}
|
||||
<tr>
|
||||
<td class="family">{{$fam}}</td>
|
||||
|
||||
{{range $j, $bucket := $.Buckets}}
|
||||
{{$n := index $.Counts $i $j}}
|
||||
<td class="{{if not $bucket.MaxErrAge}}active{{end}}{{if not $n}}empty{{end}}">
|
||||
{{if $n}}<a href="?fam={{$fam}}&b={{$j}}{{if $.Expanded}}&exp=1{{end}}">{{end}}
|
||||
[{{$n}} {{$bucket.String}}]
|
||||
{{if $n}}</a>{{end}}
|
||||
</td>
|
||||
{{end}}
|
||||
|
||||
</tr>{{end}}
|
||||
</table>
|
||||
|
||||
{{if $.EventLogs}}
|
||||
<hr />
|
||||
<h3>Family: {{$.Family}}</h3>
|
||||
|
||||
{{if $.Expanded}}<a href="?fam={{$.Family}}&b={{$.Bucket}}">{{end}}
|
||||
[Summary]{{if $.Expanded}}</a>{{end}}
|
||||
|
||||
{{if not $.Expanded}}<a href="?fam={{$.Family}}&b={{$.Bucket}}&exp=1">{{end}}
|
||||
[Expanded]{{if not $.Expanded}}</a>{{end}}
|
||||
|
||||
<table id="reqs">
|
||||
<tr><th>When</th><th>Elapsed</th></tr>
|
||||
{{range $el := $.EventLogs}}
|
||||
<tr class="first">
|
||||
<td class="when">{{$el.When}}</td>
|
||||
<td class="elapsed">{{$el.ElapsedTime}}</td>
|
||||
<td>{{$el.Title}}
|
||||
</tr>
|
||||
{{if $.Expanded}}
|
||||
<tr>
|
||||
<td class="when"></td>
|
||||
<td class="elapsed"></td>
|
||||
<td><pre>{{$el.Stack|trimSpace}}</pre></td>
|
||||
</tr>
|
||||
{{range $el.Events}}
|
||||
<tr>
|
||||
<td class="when">{{.WhenString}}</td>
|
||||
<td class="elapsed">{{elapsed .Elapsed}}</td>
|
||||
<td>.{{if .IsErr}}E{{else}}.{{end}}. {{.What}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
</table>
|
||||
{{end}}
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
356
vendor/golang.org/x/net/trace/histogram.go
generated
vendored
Normal file
356
vendor/golang.org/x/net/trace/histogram.go
generated
vendored
Normal file
|
|
@ -0,0 +1,356 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package trace
|
||||
|
||||
// This file implements histogramming for RPC statistics collection.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"log"
|
||||
"math"
|
||||
|
||||
"golang.org/x/net/internal/timeseries"
|
||||
)
|
||||
|
||||
const (
|
||||
bucketCount = 38
|
||||
)
|
||||
|
||||
// histogram keeps counts of values in buckets that are spaced
|
||||
// out in powers of 2: 0-1, 2-3, 4-7...
|
||||
// histogram implements timeseries.Observable
|
||||
type histogram struct {
|
||||
sum int64 // running total of measurements
|
||||
sumOfSquares float64 // square of running total
|
||||
buckets []int64 // bucketed values for histogram
|
||||
value int // holds a single value as an optimization
|
||||
valueCount int64 // number of values recorded for single value
|
||||
}
|
||||
|
||||
// AddMeasurement records a value measurement observation to the histogram.
|
||||
func (h *histogram) addMeasurement(value int64) {
|
||||
// TODO: assert invariant
|
||||
h.sum += value
|
||||
h.sumOfSquares += float64(value) * float64(value)
|
||||
|
||||
bucketIndex := getBucket(value)
|
||||
|
||||
if h.valueCount == 0 || (h.valueCount > 0 && h.value == bucketIndex) {
|
||||
h.value = bucketIndex
|
||||
h.valueCount++
|
||||
} else {
|
||||
h.allocateBuckets()
|
||||
h.buckets[bucketIndex]++
|
||||
}
|
||||
}
|
||||
|
||||
func (h *histogram) allocateBuckets() {
|
||||
if h.buckets == nil {
|
||||
h.buckets = make([]int64, bucketCount)
|
||||
h.buckets[h.value] = h.valueCount
|
||||
h.value = 0
|
||||
h.valueCount = -1
|
||||
}
|
||||
}
|
||||
|
||||
func log2(i int64) int {
|
||||
n := 0
|
||||
for ; i >= 0x100; i >>= 8 {
|
||||
n += 8
|
||||
}
|
||||
for ; i > 0; i >>= 1 {
|
||||
n += 1
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func getBucket(i int64) (index int) {
|
||||
index = log2(i) - 1
|
||||
if index < 0 {
|
||||
index = 0
|
||||
}
|
||||
if index >= bucketCount {
|
||||
index = bucketCount - 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Total returns the number of recorded observations.
|
||||
func (h *histogram) total() (total int64) {
|
||||
if h.valueCount >= 0 {
|
||||
total = h.valueCount
|
||||
}
|
||||
for _, val := range h.buckets {
|
||||
total += int64(val)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Average returns the average value of recorded observations.
|
||||
func (h *histogram) average() float64 {
|
||||
t := h.total()
|
||||
if t == 0 {
|
||||
return 0
|
||||
}
|
||||
return float64(h.sum) / float64(t)
|
||||
}
|
||||
|
||||
// Variance returns the variance of recorded observations.
|
||||
func (h *histogram) variance() float64 {
|
||||
t := float64(h.total())
|
||||
if t == 0 {
|
||||
return 0
|
||||
}
|
||||
s := float64(h.sum) / t
|
||||
return h.sumOfSquares/t - s*s
|
||||
}
|
||||
|
||||
// StandardDeviation returns the standard deviation of recorded observations.
|
||||
func (h *histogram) standardDeviation() float64 {
|
||||
return math.Sqrt(h.variance())
|
||||
}
|
||||
|
||||
// PercentileBoundary estimates the value that the given fraction of recorded
|
||||
// observations are less than.
|
||||
func (h *histogram) percentileBoundary(percentile float64) int64 {
|
||||
total := h.total()
|
||||
|
||||
// Corner cases (make sure result is strictly less than Total())
|
||||
if total == 0 {
|
||||
return 0
|
||||
} else if total == 1 {
|
||||
return int64(h.average())
|
||||
}
|
||||
|
||||
percentOfTotal := round(float64(total) * percentile)
|
||||
var runningTotal int64
|
||||
|
||||
for i := range h.buckets {
|
||||
value := h.buckets[i]
|
||||
runningTotal += value
|
||||
if runningTotal == percentOfTotal {
|
||||
// We hit an exact bucket boundary. If the next bucket has data, it is a
|
||||
// good estimate of the value. If the bucket is empty, we interpolate the
|
||||
// midpoint between the next bucket's boundary and the next non-zero
|
||||
// bucket. If the remaining buckets are all empty, then we use the
|
||||
// boundary for the next bucket as the estimate.
|
||||
j := uint8(i + 1)
|
||||
min := bucketBoundary(j)
|
||||
if runningTotal < total {
|
||||
for h.buckets[j] == 0 {
|
||||
j++
|
||||
}
|
||||
}
|
||||
max := bucketBoundary(j)
|
||||
return min + round(float64(max-min)/2)
|
||||
} else if runningTotal > percentOfTotal {
|
||||
// The value is in this bucket. Interpolate the value.
|
||||
delta := runningTotal - percentOfTotal
|
||||
percentBucket := float64(value-delta) / float64(value)
|
||||
bucketMin := bucketBoundary(uint8(i))
|
||||
nextBucketMin := bucketBoundary(uint8(i + 1))
|
||||
bucketSize := nextBucketMin - bucketMin
|
||||
return bucketMin + round(percentBucket*float64(bucketSize))
|
||||
}
|
||||
}
|
||||
return bucketBoundary(bucketCount - 1)
|
||||
}
|
||||
|
||||
// Median returns the estimated median of the observed values.
|
||||
func (h *histogram) median() int64 {
|
||||
return h.percentileBoundary(0.5)
|
||||
}
|
||||
|
||||
// Add adds other to h.
|
||||
func (h *histogram) Add(other timeseries.Observable) {
|
||||
o := other.(*histogram)
|
||||
if o.valueCount == 0 {
|
||||
// Other histogram is empty
|
||||
} else if h.valueCount >= 0 && o.valueCount > 0 && h.value == o.value {
|
||||
// Both have a single bucketed value, aggregate them
|
||||
h.valueCount += o.valueCount
|
||||
} else {
|
||||
// Two different values necessitate buckets in this histogram
|
||||
h.allocateBuckets()
|
||||
if o.valueCount >= 0 {
|
||||
h.buckets[o.value] += o.valueCount
|
||||
} else {
|
||||
for i := range h.buckets {
|
||||
h.buckets[i] += o.buckets[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
h.sumOfSquares += o.sumOfSquares
|
||||
h.sum += o.sum
|
||||
}
|
||||
|
||||
// Clear resets the histogram to an empty state, removing all observed values.
|
||||
func (h *histogram) Clear() {
|
||||
h.buckets = nil
|
||||
h.value = 0
|
||||
h.valueCount = 0
|
||||
h.sum = 0
|
||||
h.sumOfSquares = 0
|
||||
}
|
||||
|
||||
// CopyFrom copies from other, which must be a *histogram, into h.
|
||||
func (h *histogram) CopyFrom(other timeseries.Observable) {
|
||||
o := other.(*histogram)
|
||||
if o.valueCount == -1 {
|
||||
h.allocateBuckets()
|
||||
copy(h.buckets, o.buckets)
|
||||
}
|
||||
h.sum = o.sum
|
||||
h.sumOfSquares = o.sumOfSquares
|
||||
h.value = o.value
|
||||
h.valueCount = o.valueCount
|
||||
}
|
||||
|
||||
// Multiply scales the histogram by the specified ratio.
|
||||
func (h *histogram) Multiply(ratio float64) {
|
||||
if h.valueCount == -1 {
|
||||
for i := range h.buckets {
|
||||
h.buckets[i] = int64(float64(h.buckets[i]) * ratio)
|
||||
}
|
||||
} else {
|
||||
h.valueCount = int64(float64(h.valueCount) * ratio)
|
||||
}
|
||||
h.sum = int64(float64(h.sum) * ratio)
|
||||
h.sumOfSquares = h.sumOfSquares * ratio
|
||||
}
|
||||
|
||||
// New creates a new histogram.
|
||||
func (h *histogram) New() timeseries.Observable {
|
||||
r := new(histogram)
|
||||
r.Clear()
|
||||
return r
|
||||
}
|
||||
|
||||
func (h *histogram) String() string {
|
||||
return fmt.Sprintf("%d, %f, %d, %d, %v",
|
||||
h.sum, h.sumOfSquares, h.value, h.valueCount, h.buckets)
|
||||
}
|
||||
|
||||
// round returns the closest int64 to the argument
|
||||
func round(in float64) int64 {
|
||||
return int64(math.Floor(in + 0.5))
|
||||
}
|
||||
|
||||
// bucketBoundary returns the first value in the bucket.
|
||||
func bucketBoundary(bucket uint8) int64 {
|
||||
if bucket == 0 {
|
||||
return 0
|
||||
}
|
||||
return 1 << bucket
|
||||
}
|
||||
|
||||
// bucketData holds data about a specific bucket for use in distTmpl.
|
||||
type bucketData struct {
|
||||
Lower, Upper int64
|
||||
N int64
|
||||
Pct, CumulativePct float64
|
||||
GraphWidth int
|
||||
}
|
||||
|
||||
// data holds data about a Distribution for use in distTmpl.
|
||||
type data struct {
|
||||
Buckets []*bucketData
|
||||
Count, Median int64
|
||||
Mean, StandardDeviation float64
|
||||
}
|
||||
|
||||
// maxHTMLBarWidth is the maximum width of the HTML bar for visualizing buckets.
|
||||
const maxHTMLBarWidth = 350.0
|
||||
|
||||
// newData returns data representing h for use in distTmpl.
|
||||
func (h *histogram) newData() *data {
|
||||
// Force the allocation of buckets to simplify the rendering implementation
|
||||
h.allocateBuckets()
|
||||
// We scale the bars on the right so that the largest bar is
|
||||
// maxHTMLBarWidth pixels in width.
|
||||
maxBucket := int64(0)
|
||||
for _, n := range h.buckets {
|
||||
if n > maxBucket {
|
||||
maxBucket = n
|
||||
}
|
||||
}
|
||||
total := h.total()
|
||||
barsizeMult := maxHTMLBarWidth / float64(maxBucket)
|
||||
var pctMult float64
|
||||
if total == 0 {
|
||||
pctMult = 1.0
|
||||
} else {
|
||||
pctMult = 100.0 / float64(total)
|
||||
}
|
||||
|
||||
buckets := make([]*bucketData, len(h.buckets))
|
||||
runningTotal := int64(0)
|
||||
for i, n := range h.buckets {
|
||||
if n == 0 {
|
||||
continue
|
||||
}
|
||||
runningTotal += n
|
||||
var upperBound int64
|
||||
if i < bucketCount-1 {
|
||||
upperBound = bucketBoundary(uint8(i + 1))
|
||||
} else {
|
||||
upperBound = math.MaxInt64
|
||||
}
|
||||
buckets[i] = &bucketData{
|
||||
Lower: bucketBoundary(uint8(i)),
|
||||
Upper: upperBound,
|
||||
N: n,
|
||||
Pct: float64(n) * pctMult,
|
||||
CumulativePct: float64(runningTotal) * pctMult,
|
||||
GraphWidth: int(float64(n) * barsizeMult),
|
||||
}
|
||||
}
|
||||
return &data{
|
||||
Buckets: buckets,
|
||||
Count: total,
|
||||
Median: h.median(),
|
||||
Mean: h.average(),
|
||||
StandardDeviation: h.standardDeviation(),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *histogram) html() template.HTML {
|
||||
buf := new(bytes.Buffer)
|
||||
if err := distTmpl.Execute(buf, h.newData()); err != nil {
|
||||
buf.Reset()
|
||||
log.Printf("net/trace: couldn't execute template: %v", err)
|
||||
}
|
||||
return template.HTML(buf.String())
|
||||
}
|
||||
|
||||
// Input: data
|
||||
var distTmpl = template.Must(template.New("distTmpl").Parse(`
|
||||
<table>
|
||||
<tr>
|
||||
<td style="padding:0.25em">Count: {{.Count}}</td>
|
||||
<td style="padding:0.25em">Mean: {{printf "%.0f" .Mean}}</td>
|
||||
<td style="padding:0.25em">StdDev: {{printf "%.0f" .StandardDeviation}}</td>
|
||||
<td style="padding:0.25em">Median: {{.Median}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr>
|
||||
<table>
|
||||
{{range $b := .Buckets}}
|
||||
{{if $b}}
|
||||
<tr>
|
||||
<td style="padding:0 0 0 0.25em">[</td>
|
||||
<td style="text-align:right;padding:0 0.25em">{{.Lower}},</td>
|
||||
<td style="text-align:right;padding:0 0.25em">{{.Upper}})</td>
|
||||
<td style="text-align:right;padding:0 0.25em">{{.N}}</td>
|
||||
<td style="text-align:right;padding:0 0.25em">{{printf "%#.3f" .Pct}}%</td>
|
||||
<td style="text-align:right;padding:0 0.25em">{{printf "%#.3f" .CumulativePct}}%</td>
|
||||
<td><div style="background-color: blue; height: 1em; width: {{.GraphWidth}};"></div></td>
|
||||
</tr>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</table>
|
||||
`))
|
||||
1063
vendor/golang.org/x/net/trace/trace.go
generated
vendored
Normal file
1063
vendor/golang.org/x/net/trace/trace.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue