Add e2e tests
This commit is contained in:
parent
99a355f25d
commit
601fb7dacf
1163 changed files with 289217 additions and 14195 deletions
21
vendor/golang.org/x/crypto/ssh/client.go
generated
vendored
21
vendor/golang.org/x/crypto/ssh/client.go
generated
vendored
|
|
@ -9,6 +9,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
|
@ -187,6 +188,10 @@ func Dial(network, addr string, config *ClientConfig) (*Client, error) {
|
|||
// net.Conn underlying the the SSH connection.
|
||||
type HostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error
|
||||
|
||||
// BannerCallback is the function type used for treat the banner sent by
|
||||
// the server. A BannerCallback receives the message sent by the remote server.
|
||||
type BannerCallback func(message string) error
|
||||
|
||||
// A ClientConfig structure is used to configure a Client. It must not be
|
||||
// modified after having been passed to an SSH function.
|
||||
type ClientConfig struct {
|
||||
|
|
@ -209,6 +214,12 @@ type ClientConfig struct {
|
|||
// FixedHostKey can be used for simplistic host key checks.
|
||||
HostKeyCallback HostKeyCallback
|
||||
|
||||
// BannerCallback is called during the SSH dance to display a custom
|
||||
// server's message. The client configuration can supply this callback to
|
||||
// handle it as wished. The function BannerDisplayStderr can be used for
|
||||
// simplistic display on Stderr.
|
||||
BannerCallback BannerCallback
|
||||
|
||||
// ClientVersion contains the version identification string that will
|
||||
// be used for the connection. If empty, a reasonable default is used.
|
||||
ClientVersion string
|
||||
|
|
@ -255,3 +266,13 @@ func FixedHostKey(key PublicKey) HostKeyCallback {
|
|||
hk := &fixedHostKey{key}
|
||||
return hk.check
|
||||
}
|
||||
|
||||
// BannerDisplayStderr returns a function that can be used for
|
||||
// ClientConfig.BannerCallback to display banners on os.Stderr.
|
||||
func BannerDisplayStderr() BannerCallback {
|
||||
return func(banner string) error {
|
||||
_, err := os.Stderr.WriteString(banner)
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
|||
30
vendor/golang.org/x/crypto/ssh/client_auth.go
generated
vendored
30
vendor/golang.org/x/crypto/ssh/client_auth.go
generated
vendored
|
|
@ -283,7 +283,9 @@ func confirmKeyAck(key PublicKey, c packetConn) (bool, error) {
|
|||
}
|
||||
switch packet[0] {
|
||||
case msgUserAuthBanner:
|
||||
// TODO(gpaul): add callback to present the banner to the user
|
||||
if err := handleBannerResponse(c, packet); err != nil {
|
||||
return false, err
|
||||
}
|
||||
case msgUserAuthPubKeyOk:
|
||||
var msg userAuthPubKeyOkMsg
|
||||
if err := Unmarshal(packet, &msg); err != nil {
|
||||
|
|
@ -325,7 +327,9 @@ func handleAuthResponse(c packetConn) (bool, []string, error) {
|
|||
|
||||
switch packet[0] {
|
||||
case msgUserAuthBanner:
|
||||
// TODO: add callback to present the banner to the user
|
||||
if err := handleBannerResponse(c, packet); err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
case msgUserAuthFailure:
|
||||
var msg userAuthFailureMsg
|
||||
if err := Unmarshal(packet, &msg); err != nil {
|
||||
|
|
@ -340,6 +344,24 @@ func handleAuthResponse(c packetConn) (bool, []string, error) {
|
|||
}
|
||||
}
|
||||
|
||||
func handleBannerResponse(c packetConn, packet []byte) error {
|
||||
var msg userAuthBannerMsg
|
||||
if err := Unmarshal(packet, &msg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
transport, ok := c.(*handshakeTransport)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
if transport.bannerCallback != nil {
|
||||
return transport.bannerCallback(msg.Message)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// KeyboardInteractiveChallenge should print questions, optionally
|
||||
// disabling echoing (e.g. for passwords), and return all the answers.
|
||||
// Challenge may be called multiple times in a single session. After
|
||||
|
|
@ -385,7 +407,9 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
|
|||
// like handleAuthResponse, but with less options.
|
||||
switch packet[0] {
|
||||
case msgUserAuthBanner:
|
||||
// TODO: Print banners during userauth.
|
||||
if err := handleBannerResponse(c, packet); err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
continue
|
||||
case msgUserAuthInfoRequest:
|
||||
// OK
|
||||
|
|
|
|||
37
vendor/golang.org/x/crypto/ssh/client_test.go
generated
vendored
37
vendor/golang.org/x/crypto/ssh/client_test.go
generated
vendored
|
|
@ -79,3 +79,40 @@ func TestHostKeyCheck(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
func TestBannerCallback(t *testing.T) {
|
||||
c1, c2, err := netPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("netPipe: %v", err)
|
||||
}
|
||||
defer c1.Close()
|
||||
defer c2.Close()
|
||||
|
||||
serverConf := &ServerConfig{
|
||||
NoClientAuth: true,
|
||||
BannerCallback: func(conn ConnMetadata) string {
|
||||
return "Hello World"
|
||||
},
|
||||
}
|
||||
serverConf.AddHostKey(testSigners["rsa"])
|
||||
go NewServerConn(c1, serverConf)
|
||||
|
||||
var receivedBanner string
|
||||
clientConf := ClientConfig{
|
||||
User: "user",
|
||||
HostKeyCallback: InsecureIgnoreHostKey(),
|
||||
BannerCallback: func(message string) error {
|
||||
receivedBanner = message
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
_, _, _, err = NewClientConn(c2, "", &clientConf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := "Hello World"
|
||||
if receivedBanner != expected {
|
||||
t.Fatalf("got %s; want %s", receivedBanner, expected)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
6
vendor/golang.org/x/crypto/ssh/handshake.go
generated
vendored
6
vendor/golang.org/x/crypto/ssh/handshake.go
generated
vendored
|
|
@ -78,6 +78,11 @@ type handshakeTransport struct {
|
|||
dialAddress string
|
||||
remoteAddr net.Addr
|
||||
|
||||
// bannerCallback is non-empty if we are the client and it has been set in
|
||||
// ClientConfig. In that case it is called during the user authentication
|
||||
// dance to handle a custom server's message.
|
||||
bannerCallback BannerCallback
|
||||
|
||||
// Algorithms agreed in the last key exchange.
|
||||
algorithms *algorithms
|
||||
|
||||
|
|
@ -120,6 +125,7 @@ func newClientTransport(conn keyingTransport, clientVersion, serverVersion []byt
|
|||
t.dialAddress = dialAddr
|
||||
t.remoteAddr = addr
|
||||
t.hostKeyCallback = config.HostKeyCallback
|
||||
t.bannerCallback = config.BannerCallback
|
||||
if config.HostKeyAlgorithms != nil {
|
||||
t.hostKeyAlgorithms = config.HostKeyAlgorithms
|
||||
} else {
|
||||
|
|
|
|||
14
vendor/golang.org/x/crypto/ssh/messages.go
generated
vendored
14
vendor/golang.org/x/crypto/ssh/messages.go
generated
vendored
|
|
@ -23,10 +23,6 @@ const (
|
|||
msgUnimplemented = 3
|
||||
msgDebug = 4
|
||||
msgNewKeys = 21
|
||||
|
||||
// Standard authentication messages
|
||||
msgUserAuthSuccess = 52
|
||||
msgUserAuthBanner = 53
|
||||
)
|
||||
|
||||
// SSH messages:
|
||||
|
|
@ -137,6 +133,16 @@ type userAuthFailureMsg struct {
|
|||
PartialSuccess bool
|
||||
}
|
||||
|
||||
// See RFC 4252, section 5.1
|
||||
const msgUserAuthSuccess = 52
|
||||
|
||||
// See RFC 4252, section 5.4
|
||||
const msgUserAuthBanner = 53
|
||||
|
||||
type userAuthBannerMsg struct {
|
||||
Message string `sshtype:"53"`
|
||||
}
|
||||
|
||||
// See RFC 4256, section 3.2
|
||||
const msgUserAuthInfoRequest = 60
|
||||
const msgUserAuthInfoResponse = 61
|
||||
|
|
|
|||
17
vendor/golang.org/x/crypto/ssh/server.go
generated
vendored
17
vendor/golang.org/x/crypto/ssh/server.go
generated
vendored
|
|
@ -95,6 +95,10 @@ type ServerConfig struct {
|
|||
// Note that RFC 4253 section 4.2 requires that this string start with
|
||||
// "SSH-2.0-".
|
||||
ServerVersion string
|
||||
|
||||
// BannerCallback, if present, is called and the return string is sent to
|
||||
// the client after key exchange completed but before authentication.
|
||||
BannerCallback func(conn ConnMetadata) string
|
||||
}
|
||||
|
||||
// AddHostKey adds a private key as a host key. If an existing host
|
||||
|
|
@ -343,6 +347,19 @@ userAuthLoop:
|
|||
}
|
||||
|
||||
s.user = userAuthReq.User
|
||||
|
||||
if authFailures == 0 && config.BannerCallback != nil {
|
||||
msg := config.BannerCallback(s)
|
||||
if msg != "" {
|
||||
bannerMsg := &userAuthBannerMsg{
|
||||
Message: msg,
|
||||
}
|
||||
if err := s.transport.writePacket(Marshal(bannerMsg)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
perms = nil
|
||||
authErr := errors.New("no auth passed yet")
|
||||
|
||||
|
|
|
|||
74
vendor/golang.org/x/net/context/ctxhttp/ctxhttp.go
generated
vendored
Normal file
74
vendor/golang.org/x/net/context/ctxhttp/ctxhttp.go
generated
vendored
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
// 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 // import "golang.org/x/net/context/ctxhttp"
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// Do sends an HTTP request with the provided http.Client and returns
|
||||
// an HTTP response.
|
||||
//
|
||||
// If the client is nil, http.DefaultClient is used.
|
||||
//
|
||||
// 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
|
||||
}
|
||||
resp, err := client.Do(req.WithContext(ctx))
|
||||
// If we got an error, and the context has been canceled,
|
||||
// the context's error is probably more useful.
|
||||
if err != nil {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err = ctx.Err()
|
||||
default:
|
||||
}
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// 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()))
|
||||
}
|
||||
29
vendor/golang.org/x/net/context/ctxhttp/ctxhttp_17_test.go
generated
vendored
Normal file
29
vendor/golang.org/x/net/context/ctxhttp/ctxhttp_17_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
// 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 !plan9,go1.7
|
||||
|
||||
package ctxhttp
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"context"
|
||||
)
|
||||
|
||||
func TestGo17Context(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
io.WriteString(w, "ok")
|
||||
}))
|
||||
defer ts.Close()
|
||||
ctx := context.Background()
|
||||
resp, err := Get(ctx, http.DefaultClient, ts.URL)
|
||||
if resp == nil || err != nil {
|
||||
t.Fatalf("error received from client: %v %v", err, resp)
|
||||
}
|
||||
resp.Body.Close()
|
||||
}
|
||||
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 "golang.org/x/net/context/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
|
||||
}
|
||||
79
vendor/golang.org/x/net/context/ctxhttp/ctxhttp_pre17_test.go
generated
vendored
Normal file
79
vendor/golang.org/x/net/context/ctxhttp/ctxhttp_pre17_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
// 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 !plan9,!go1.7
|
||||
|
||||
package ctxhttp
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// golang.org/issue/14065
|
||||
func TestClosesResponseBodyOnCancel(t *testing.T) {
|
||||
defer func() { testHookContextDoneBeforeHeaders = nop }()
|
||||
defer func() { testHookDoReturned = nop }()
|
||||
defer func() { testHookDidBodyClose = nop }()
|
||||
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
||||
defer ts.Close()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
// closed when Do enters select case <-ctx.Done()
|
||||
enteredDonePath := make(chan struct{})
|
||||
|
||||
testHookContextDoneBeforeHeaders = func() {
|
||||
close(enteredDonePath)
|
||||
}
|
||||
|
||||
testHookDoReturned = func() {
|
||||
// We now have the result (the Flush'd headers) at least,
|
||||
// so we can cancel the request.
|
||||
cancel()
|
||||
|
||||
// But block the client.Do goroutine from sending
|
||||
// until Do enters into the <-ctx.Done() path, since
|
||||
// otherwise if both channels are readable, select
|
||||
// picks a random one.
|
||||
<-enteredDonePath
|
||||
}
|
||||
|
||||
sawBodyClose := make(chan struct{})
|
||||
testHookDidBodyClose = func() { close(sawBodyClose) }
|
||||
|
||||
tr := &http.Transport{}
|
||||
defer tr.CloseIdleConnections()
|
||||
c := &http.Client{Transport: tr}
|
||||
req, _ := http.NewRequest("GET", ts.URL, nil)
|
||||
_, doErr := Do(ctx, c, req)
|
||||
|
||||
select {
|
||||
case <-sawBodyClose:
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("timeout waiting for body to close")
|
||||
}
|
||||
|
||||
if doErr != ctx.Err() {
|
||||
t.Errorf("Do error = %v; want %v", doErr, ctx.Err())
|
||||
}
|
||||
}
|
||||
|
||||
type noteCloseConn struct {
|
||||
net.Conn
|
||||
onceClose sync.Once
|
||||
closefn func()
|
||||
}
|
||||
|
||||
func (c *noteCloseConn) Close() error {
|
||||
c.onceClose.Do(c.closefn)
|
||||
return c.Conn.Close()
|
||||
}
|
||||
105
vendor/golang.org/x/net/context/ctxhttp/ctxhttp_test.go
generated
vendored
Normal file
105
vendor/golang.org/x/net/context/ctxhttp/ctxhttp_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
// 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 !plan9
|
||||
|
||||
package ctxhttp
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
const (
|
||||
requestDuration = 100 * time.Millisecond
|
||||
requestBody = "ok"
|
||||
)
|
||||
|
||||
func okHandler(w http.ResponseWriter, r *http.Request) {
|
||||
time.Sleep(requestDuration)
|
||||
io.WriteString(w, requestBody)
|
||||
}
|
||||
|
||||
func TestNoTimeout(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(okHandler))
|
||||
defer ts.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
res, err := Get(ctx, nil, ts.URL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
slurp, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(slurp) != requestBody {
|
||||
t.Errorf("body = %q; want %q", slurp, requestBody)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCancelBeforeHeaders(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
blockServer := make(chan struct{})
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
cancel()
|
||||
<-blockServer
|
||||
io.WriteString(w, requestBody)
|
||||
}))
|
||||
defer ts.Close()
|
||||
defer close(blockServer)
|
||||
|
||||
res, err := Get(ctx, nil, ts.URL)
|
||||
if err == nil {
|
||||
res.Body.Close()
|
||||
t.Fatal("Get returned unexpected nil error")
|
||||
}
|
||||
if err != context.Canceled {
|
||||
t.Errorf("err = %v; want %v", err, context.Canceled)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCancelAfterHangingRequest(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.(http.Flusher).Flush()
|
||||
<-w.(http.CloseNotifier).CloseNotify()
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
resp, err := Get(ctx, nil, ts.URL)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error in Get: %v", err)
|
||||
}
|
||||
|
||||
// Cancel befer reading the body.
|
||||
// Reading Request.Body should fail, since the request was
|
||||
// canceled before anything was written.
|
||||
cancel()
|
||||
|
||||
done := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
if len(b) != 0 || err == nil {
|
||||
t.Errorf(`Read got (%q, %v); want ("", error)`, b, err)
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-time.After(1 * time.Second):
|
||||
t.Errorf("Test timed out")
|
||||
case <-done:
|
||||
}
|
||||
}
|
||||
78
vendor/golang.org/x/net/html/atom/atom.go
generated
vendored
Normal file
78
vendor/golang.org/x/net/html/atom/atom.go
generated
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
// Copyright 2012 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 atom provides integer codes (also known as atoms) for a fixed set of
|
||||
// frequently occurring HTML strings: tag names and attribute keys such as "p"
|
||||
// and "id".
|
||||
//
|
||||
// Sharing an atom's name between all elements with the same tag can result in
|
||||
// fewer string allocations when tokenizing and parsing HTML. Integer
|
||||
// comparisons are also generally faster than string comparisons.
|
||||
//
|
||||
// The value of an atom's particular code is not guaranteed to stay the same
|
||||
// between versions of this package. Neither is any ordering guaranteed:
|
||||
// whether atom.H1 < atom.H2 may also change. The codes are not guaranteed to
|
||||
// be dense. The only guarantees are that e.g. looking up "div" will yield
|
||||
// atom.Div, calling atom.Div.String will return "div", and atom.Div != 0.
|
||||
package atom // import "golang.org/x/net/html/atom"
|
||||
|
||||
// Atom is an integer code for a string. The zero value maps to "".
|
||||
type Atom uint32
|
||||
|
||||
// String returns the atom's name.
|
||||
func (a Atom) String() string {
|
||||
start := uint32(a >> 8)
|
||||
n := uint32(a & 0xff)
|
||||
if start+n > uint32(len(atomText)) {
|
||||
return ""
|
||||
}
|
||||
return atomText[start : start+n]
|
||||
}
|
||||
|
||||
func (a Atom) string() string {
|
||||
return atomText[a>>8 : a>>8+a&0xff]
|
||||
}
|
||||
|
||||
// fnv computes the FNV hash with an arbitrary starting value h.
|
||||
func fnv(h uint32, s []byte) uint32 {
|
||||
for i := range s {
|
||||
h ^= uint32(s[i])
|
||||
h *= 16777619
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
func match(s string, t []byte) bool {
|
||||
for i, c := range t {
|
||||
if s[i] != c {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Lookup returns the atom whose name is s. It returns zero if there is no
|
||||
// such atom. The lookup is case sensitive.
|
||||
func Lookup(s []byte) Atom {
|
||||
if len(s) == 0 || len(s) > maxAtomLen {
|
||||
return 0
|
||||
}
|
||||
h := fnv(hash0, s)
|
||||
if a := table[h&uint32(len(table)-1)]; int(a&0xff) == len(s) && match(a.string(), s) {
|
||||
return a
|
||||
}
|
||||
if a := table[(h>>16)&uint32(len(table)-1)]; int(a&0xff) == len(s) && match(a.string(), s) {
|
||||
return a
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// String returns a string whose contents are equal to s. In that sense, it is
|
||||
// equivalent to string(s) but may be more efficient.
|
||||
func String(s []byte) string {
|
||||
if a := Lookup(s); a != 0 {
|
||||
return a.String()
|
||||
}
|
||||
return string(s)
|
||||
}
|
||||
109
vendor/golang.org/x/net/html/atom/atom_test.go
generated
vendored
Normal file
109
vendor/golang.org/x/net/html/atom/atom_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
// Copyright 2012 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 atom
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestKnown(t *testing.T) {
|
||||
for _, s := range testAtomList {
|
||||
if atom := Lookup([]byte(s)); atom.String() != s {
|
||||
t.Errorf("Lookup(%q) = %#x (%q)", s, uint32(atom), atom.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHits(t *testing.T) {
|
||||
for _, a := range table {
|
||||
if a == 0 {
|
||||
continue
|
||||
}
|
||||
got := Lookup([]byte(a.String()))
|
||||
if got != a {
|
||||
t.Errorf("Lookup(%q) = %#x, want %#x", a.String(), uint32(got), uint32(a))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMisses(t *testing.T) {
|
||||
testCases := []string{
|
||||
"",
|
||||
"\x00",
|
||||
"\xff",
|
||||
"A",
|
||||
"DIV",
|
||||
"Div",
|
||||
"dIV",
|
||||
"aa",
|
||||
"a\x00",
|
||||
"ab",
|
||||
"abb",
|
||||
"abbr0",
|
||||
"abbr ",
|
||||
" abbr",
|
||||
" a",
|
||||
"acceptcharset",
|
||||
"acceptCharset",
|
||||
"accept_charset",
|
||||
"h0",
|
||||
"h1h2",
|
||||
"h7",
|
||||
"onClick",
|
||||
"λ",
|
||||
// The following string has the same hash (0xa1d7fab7) as "onmouseover".
|
||||
"\x00\x00\x00\x00\x00\x50\x18\xae\x38\xd0\xb7",
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
got := Lookup([]byte(tc))
|
||||
if got != 0 {
|
||||
t.Errorf("Lookup(%q): got %d, want 0", tc, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestForeignObject(t *testing.T) {
|
||||
const (
|
||||
afo = Foreignobject
|
||||
afO = ForeignObject
|
||||
sfo = "foreignobject"
|
||||
sfO = "foreignObject"
|
||||
)
|
||||
if got := Lookup([]byte(sfo)); got != afo {
|
||||
t.Errorf("Lookup(%q): got %#v, want %#v", sfo, got, afo)
|
||||
}
|
||||
if got := Lookup([]byte(sfO)); got != afO {
|
||||
t.Errorf("Lookup(%q): got %#v, want %#v", sfO, got, afO)
|
||||
}
|
||||
if got := afo.String(); got != sfo {
|
||||
t.Errorf("Atom(%#v).String(): got %q, want %q", afo, got, sfo)
|
||||
}
|
||||
if got := afO.String(); got != sfO {
|
||||
t.Errorf("Atom(%#v).String(): got %q, want %q", afO, got, sfO)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLookup(b *testing.B) {
|
||||
sortedTable := make([]string, 0, len(table))
|
||||
for _, a := range table {
|
||||
if a != 0 {
|
||||
sortedTable = append(sortedTable, a.String())
|
||||
}
|
||||
}
|
||||
sort.Strings(sortedTable)
|
||||
|
||||
x := make([][]byte, 1000)
|
||||
for i := range x {
|
||||
x[i] = []byte(sortedTable[i%len(sortedTable)])
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, s := range x {
|
||||
Lookup(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
709
vendor/golang.org/x/net/html/atom/gen.go
generated
vendored
Normal file
709
vendor/golang.org/x/net/html/atom/gen.go
generated
vendored
Normal file
|
|
@ -0,0 +1,709 @@
|
|||
// Copyright 2012 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 ignore
|
||||
|
||||
//go:generate go run gen.go
|
||||
//go:generate go run gen.go -test
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// identifier converts s to a Go exported identifier.
|
||||
// It converts "div" to "Div" and "accept-charset" to "AcceptCharset".
|
||||
func identifier(s string) string {
|
||||
b := make([]byte, 0, len(s))
|
||||
cap := true
|
||||
for _, c := range s {
|
||||
if c == '-' {
|
||||
cap = true
|
||||
continue
|
||||
}
|
||||
if cap && 'a' <= c && c <= 'z' {
|
||||
c -= 'a' - 'A'
|
||||
}
|
||||
cap = false
|
||||
b = append(b, byte(c))
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
var test = flag.Bool("test", false, "generate table_test.go")
|
||||
|
||||
func genFile(name string, buf *bytes.Buffer) {
|
||||
b, err := format.Source(buf.Bytes())
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := ioutil.WriteFile(name, b, 0644); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
var all []string
|
||||
all = append(all, elements...)
|
||||
all = append(all, attributes...)
|
||||
all = append(all, eventHandlers...)
|
||||
all = append(all, extra...)
|
||||
sort.Strings(all)
|
||||
|
||||
// uniq - lists have dups
|
||||
w := 0
|
||||
for _, s := range all {
|
||||
if w == 0 || all[w-1] != s {
|
||||
all[w] = s
|
||||
w++
|
||||
}
|
||||
}
|
||||
all = all[:w]
|
||||
|
||||
if *test {
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprintln(&buf, "// Code generated by go generate gen.go; DO NOT EDIT.\n")
|
||||
fmt.Fprintln(&buf, "//go:generate go run gen.go -test\n")
|
||||
fmt.Fprintln(&buf, "package atom\n")
|
||||
fmt.Fprintln(&buf, "var testAtomList = []string{")
|
||||
for _, s := range all {
|
||||
fmt.Fprintf(&buf, "\t%q,\n", s)
|
||||
}
|
||||
fmt.Fprintln(&buf, "}")
|
||||
|
||||
genFile("table_test.go", &buf)
|
||||
return
|
||||
}
|
||||
|
||||
// Find hash that minimizes table size.
|
||||
var best *table
|
||||
for i := 0; i < 1000000; i++ {
|
||||
if best != nil && 1<<(best.k-1) < len(all) {
|
||||
break
|
||||
}
|
||||
h := rand.Uint32()
|
||||
for k := uint(0); k <= 16; k++ {
|
||||
if best != nil && k >= best.k {
|
||||
break
|
||||
}
|
||||
var t table
|
||||
if t.init(h, k, all) {
|
||||
best = &t
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if best == nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to construct string table\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Lay out strings, using overlaps when possible.
|
||||
layout := append([]string{}, all...)
|
||||
|
||||
// Remove strings that are substrings of other strings
|
||||
for changed := true; changed; {
|
||||
changed = false
|
||||
for i, s := range layout {
|
||||
if s == "" {
|
||||
continue
|
||||
}
|
||||
for j, t := range layout {
|
||||
if i != j && t != "" && strings.Contains(s, t) {
|
||||
changed = true
|
||||
layout[j] = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Join strings where one suffix matches another prefix.
|
||||
for {
|
||||
// Find best i, j, k such that layout[i][len-k:] == layout[j][:k],
|
||||
// maximizing overlap length k.
|
||||
besti := -1
|
||||
bestj := -1
|
||||
bestk := 0
|
||||
for i, s := range layout {
|
||||
if s == "" {
|
||||
continue
|
||||
}
|
||||
for j, t := range layout {
|
||||
if i == j {
|
||||
continue
|
||||
}
|
||||
for k := bestk + 1; k <= len(s) && k <= len(t); k++ {
|
||||
if s[len(s)-k:] == t[:k] {
|
||||
besti = i
|
||||
bestj = j
|
||||
bestk = k
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if bestk > 0 {
|
||||
layout[besti] += layout[bestj][bestk:]
|
||||
layout[bestj] = ""
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
text := strings.Join(layout, "")
|
||||
|
||||
atom := map[string]uint32{}
|
||||
for _, s := range all {
|
||||
off := strings.Index(text, s)
|
||||
if off < 0 {
|
||||
panic("lost string " + s)
|
||||
}
|
||||
atom[s] = uint32(off<<8 | len(s))
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
// Generate the Go code.
|
||||
fmt.Fprintln(&buf, "// Code generated by go generate gen.go; DO NOT EDIT.\n")
|
||||
fmt.Fprintln(&buf, "//go:generate go run gen.go\n")
|
||||
fmt.Fprintln(&buf, "package atom\n\nconst (")
|
||||
|
||||
// compute max len
|
||||
maxLen := 0
|
||||
for _, s := range all {
|
||||
if maxLen < len(s) {
|
||||
maxLen = len(s)
|
||||
}
|
||||
fmt.Fprintf(&buf, "\t%s Atom = %#x\n", identifier(s), atom[s])
|
||||
}
|
||||
fmt.Fprintln(&buf, ")\n")
|
||||
|
||||
fmt.Fprintf(&buf, "const hash0 = %#x\n\n", best.h0)
|
||||
fmt.Fprintf(&buf, "const maxAtomLen = %d\n\n", maxLen)
|
||||
|
||||
fmt.Fprintf(&buf, "var table = [1<<%d]Atom{\n", best.k)
|
||||
for i, s := range best.tab {
|
||||
if s == "" {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(&buf, "\t%#x: %#x, // %s\n", i, atom[s], s)
|
||||
}
|
||||
fmt.Fprintf(&buf, "}\n")
|
||||
datasize := (1 << best.k) * 4
|
||||
|
||||
fmt.Fprintln(&buf, "const atomText =")
|
||||
textsize := len(text)
|
||||
for len(text) > 60 {
|
||||
fmt.Fprintf(&buf, "\t%q +\n", text[:60])
|
||||
text = text[60:]
|
||||
}
|
||||
fmt.Fprintf(&buf, "\t%q\n\n", text)
|
||||
|
||||
genFile("table.go", &buf)
|
||||
|
||||
fmt.Fprintf(os.Stdout, "%d atoms; %d string bytes + %d tables = %d total data\n", len(all), textsize, datasize, textsize+datasize)
|
||||
}
|
||||
|
||||
type byLen []string
|
||||
|
||||
func (x byLen) Less(i, j int) bool { return len(x[i]) > len(x[j]) }
|
||||
func (x byLen) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||
func (x byLen) Len() int { return len(x) }
|
||||
|
||||
// fnv computes the FNV hash with an arbitrary starting value h.
|
||||
func fnv(h uint32, s string) uint32 {
|
||||
for i := 0; i < len(s); i++ {
|
||||
h ^= uint32(s[i])
|
||||
h *= 16777619
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
// A table represents an attempt at constructing the lookup table.
|
||||
// The lookup table uses cuckoo hashing, meaning that each string
|
||||
// can be found in one of two positions.
|
||||
type table struct {
|
||||
h0 uint32
|
||||
k uint
|
||||
mask uint32
|
||||
tab []string
|
||||
}
|
||||
|
||||
// hash returns the two hashes for s.
|
||||
func (t *table) hash(s string) (h1, h2 uint32) {
|
||||
h := fnv(t.h0, s)
|
||||
h1 = h & t.mask
|
||||
h2 = (h >> 16) & t.mask
|
||||
return
|
||||
}
|
||||
|
||||
// init initializes the table with the given parameters.
|
||||
// h0 is the initial hash value,
|
||||
// k is the number of bits of hash value to use, and
|
||||
// x is the list of strings to store in the table.
|
||||
// init returns false if the table cannot be constructed.
|
||||
func (t *table) init(h0 uint32, k uint, x []string) bool {
|
||||
t.h0 = h0
|
||||
t.k = k
|
||||
t.tab = make([]string, 1<<k)
|
||||
t.mask = 1<<k - 1
|
||||
for _, s := range x {
|
||||
if !t.insert(s) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// insert inserts s in the table.
|
||||
func (t *table) insert(s string) bool {
|
||||
h1, h2 := t.hash(s)
|
||||
if t.tab[h1] == "" {
|
||||
t.tab[h1] = s
|
||||
return true
|
||||
}
|
||||
if t.tab[h2] == "" {
|
||||
t.tab[h2] = s
|
||||
return true
|
||||
}
|
||||
if t.push(h1, 0) {
|
||||
t.tab[h1] = s
|
||||
return true
|
||||
}
|
||||
if t.push(h2, 0) {
|
||||
t.tab[h2] = s
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// push attempts to push aside the entry in slot i.
|
||||
func (t *table) push(i uint32, depth int) bool {
|
||||
if depth > len(t.tab) {
|
||||
return false
|
||||
}
|
||||
s := t.tab[i]
|
||||
h1, h2 := t.hash(s)
|
||||
j := h1 + h2 - i
|
||||
if t.tab[j] != "" && !t.push(j, depth+1) {
|
||||
return false
|
||||
}
|
||||
t.tab[j] = s
|
||||
return true
|
||||
}
|
||||
|
||||
// The lists of element names and attribute keys were taken from
|
||||
// https://html.spec.whatwg.org/multipage/indices.html#index
|
||||
// as of the "HTML Living Standard - Last Updated 18 September 2017" version.
|
||||
|
||||
// "command", "keygen" and "menuitem" have been removed from the spec,
|
||||
// but are kept here for backwards compatibility.
|
||||
var elements = []string{
|
||||
"a",
|
||||
"abbr",
|
||||
"address",
|
||||
"area",
|
||||
"article",
|
||||
"aside",
|
||||
"audio",
|
||||
"b",
|
||||
"base",
|
||||
"bdi",
|
||||
"bdo",
|
||||
"blockquote",
|
||||
"body",
|
||||
"br",
|
||||
"button",
|
||||
"canvas",
|
||||
"caption",
|
||||
"cite",
|
||||
"code",
|
||||
"col",
|
||||
"colgroup",
|
||||
"command",
|
||||
"data",
|
||||
"datalist",
|
||||
"dd",
|
||||
"del",
|
||||
"details",
|
||||
"dfn",
|
||||
"dialog",
|
||||
"div",
|
||||
"dl",
|
||||
"dt",
|
||||
"em",
|
||||
"embed",
|
||||
"fieldset",
|
||||
"figcaption",
|
||||
"figure",
|
||||
"footer",
|
||||
"form",
|
||||
"h1",
|
||||
"h2",
|
||||
"h3",
|
||||
"h4",
|
||||
"h5",
|
||||
"h6",
|
||||
"head",
|
||||
"header",
|
||||
"hgroup",
|
||||
"hr",
|
||||
"html",
|
||||
"i",
|
||||
"iframe",
|
||||
"img",
|
||||
"input",
|
||||
"ins",
|
||||
"kbd",
|
||||
"keygen",
|
||||
"label",
|
||||
"legend",
|
||||
"li",
|
||||
"link",
|
||||
"main",
|
||||
"map",
|
||||
"mark",
|
||||
"menu",
|
||||
"menuitem",
|
||||
"meta",
|
||||
"meter",
|
||||
"nav",
|
||||
"noscript",
|
||||
"object",
|
||||
"ol",
|
||||
"optgroup",
|
||||
"option",
|
||||
"output",
|
||||
"p",
|
||||
"param",
|
||||
"picture",
|
||||
"pre",
|
||||
"progress",
|
||||
"q",
|
||||
"rp",
|
||||
"rt",
|
||||
"ruby",
|
||||
"s",
|
||||
"samp",
|
||||
"script",
|
||||
"section",
|
||||
"select",
|
||||
"slot",
|
||||
"small",
|
||||
"source",
|
||||
"span",
|
||||
"strong",
|
||||
"style",
|
||||
"sub",
|
||||
"summary",
|
||||
"sup",
|
||||
"table",
|
||||
"tbody",
|
||||
"td",
|
||||
"template",
|
||||
"textarea",
|
||||
"tfoot",
|
||||
"th",
|
||||
"thead",
|
||||
"time",
|
||||
"title",
|
||||
"tr",
|
||||
"track",
|
||||
"u",
|
||||
"ul",
|
||||
"var",
|
||||
"video",
|
||||
"wbr",
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/indices.html#attributes-3
|
||||
//
|
||||
// "challenge", "command", "contextmenu", "dropzone", "icon", "keytype", "mediagroup",
|
||||
// "radiogroup", "spellcheck", "scoped", "seamless", "sortable" and "sorted" have been removed from the spec,
|
||||
// but are kept here for backwards compatibility.
|
||||
var attributes = []string{
|
||||
"abbr",
|
||||
"accept",
|
||||
"accept-charset",
|
||||
"accesskey",
|
||||
"action",
|
||||
"allowfullscreen",
|
||||
"allowpaymentrequest",
|
||||
"allowusermedia",
|
||||
"alt",
|
||||
"as",
|
||||
"async",
|
||||
"autocomplete",
|
||||
"autofocus",
|
||||
"autoplay",
|
||||
"challenge",
|
||||
"charset",
|
||||
"checked",
|
||||
"cite",
|
||||
"class",
|
||||
"color",
|
||||
"cols",
|
||||
"colspan",
|
||||
"command",
|
||||
"content",
|
||||
"contenteditable",
|
||||
"contextmenu",
|
||||
"controls",
|
||||
"coords",
|
||||
"crossorigin",
|
||||
"data",
|
||||
"datetime",
|
||||
"default",
|
||||
"defer",
|
||||
"dir",
|
||||
"dirname",
|
||||
"disabled",
|
||||
"download",
|
||||
"draggable",
|
||||
"dropzone",
|
||||
"enctype",
|
||||
"for",
|
||||
"form",
|
||||
"formaction",
|
||||
"formenctype",
|
||||
"formmethod",
|
||||
"formnovalidate",
|
||||
"formtarget",
|
||||
"headers",
|
||||
"height",
|
||||
"hidden",
|
||||
"high",
|
||||
"href",
|
||||
"hreflang",
|
||||
"http-equiv",
|
||||
"icon",
|
||||
"id",
|
||||
"inputmode",
|
||||
"integrity",
|
||||
"is",
|
||||
"ismap",
|
||||
"itemid",
|
||||
"itemprop",
|
||||
"itemref",
|
||||
"itemscope",
|
||||
"itemtype",
|
||||
"keytype",
|
||||
"kind",
|
||||
"label",
|
||||
"lang",
|
||||
"list",
|
||||
"loop",
|
||||
"low",
|
||||
"manifest",
|
||||
"max",
|
||||
"maxlength",
|
||||
"media",
|
||||
"mediagroup",
|
||||
"method",
|
||||
"min",
|
||||
"minlength",
|
||||
"multiple",
|
||||
"muted",
|
||||
"name",
|
||||
"nomodule",
|
||||
"nonce",
|
||||
"novalidate",
|
||||
"open",
|
||||
"optimum",
|
||||
"pattern",
|
||||
"ping",
|
||||
"placeholder",
|
||||
"playsinline",
|
||||
"poster",
|
||||
"preload",
|
||||
"radiogroup",
|
||||
"readonly",
|
||||
"referrerpolicy",
|
||||
"rel",
|
||||
"required",
|
||||
"reversed",
|
||||
"rows",
|
||||
"rowspan",
|
||||
"sandbox",
|
||||
"spellcheck",
|
||||
"scope",
|
||||
"scoped",
|
||||
"seamless",
|
||||
"selected",
|
||||
"shape",
|
||||
"size",
|
||||
"sizes",
|
||||
"sortable",
|
||||
"sorted",
|
||||
"slot",
|
||||
"span",
|
||||
"spellcheck",
|
||||
"src",
|
||||
"srcdoc",
|
||||
"srclang",
|
||||
"srcset",
|
||||
"start",
|
||||
"step",
|
||||
"style",
|
||||
"tabindex",
|
||||
"target",
|
||||
"title",
|
||||
"translate",
|
||||
"type",
|
||||
"typemustmatch",
|
||||
"updateviacache",
|
||||
"usemap",
|
||||
"value",
|
||||
"width",
|
||||
"workertype",
|
||||
"wrap",
|
||||
}
|
||||
|
||||
// "onautocomplete", "onautocompleteerror", "onmousewheel",
|
||||
// "onshow" and "onsort" have been removed from the spec,
|
||||
// but are kept here for backwards compatibility.
|
||||
var eventHandlers = []string{
|
||||
"onabort",
|
||||
"onautocomplete",
|
||||
"onautocompleteerror",
|
||||
"onauxclick",
|
||||
"onafterprint",
|
||||
"onbeforeprint",
|
||||
"onbeforeunload",
|
||||
"onblur",
|
||||
"oncancel",
|
||||
"oncanplay",
|
||||
"oncanplaythrough",
|
||||
"onchange",
|
||||
"onclick",
|
||||
"onclose",
|
||||
"oncontextmenu",
|
||||
"oncopy",
|
||||
"oncuechange",
|
||||
"oncut",
|
||||
"ondblclick",
|
||||
"ondrag",
|
||||
"ondragend",
|
||||
"ondragenter",
|
||||
"ondragexit",
|
||||
"ondragleave",
|
||||
"ondragover",
|
||||
"ondragstart",
|
||||
"ondrop",
|
||||
"ondurationchange",
|
||||
"onemptied",
|
||||
"onended",
|
||||
"onerror",
|
||||
"onfocus",
|
||||
"onhashchange",
|
||||
"oninput",
|
||||
"oninvalid",
|
||||
"onkeydown",
|
||||
"onkeypress",
|
||||
"onkeyup",
|
||||
"onlanguagechange",
|
||||
"onload",
|
||||
"onloadeddata",
|
||||
"onloadedmetadata",
|
||||
"onloadend",
|
||||
"onloadstart",
|
||||
"onmessage",
|
||||
"onmessageerror",
|
||||
"onmousedown",
|
||||
"onmouseenter",
|
||||
"onmouseleave",
|
||||
"onmousemove",
|
||||
"onmouseout",
|
||||
"onmouseover",
|
||||
"onmouseup",
|
||||
"onmousewheel",
|
||||
"onwheel",
|
||||
"onoffline",
|
||||
"ononline",
|
||||
"onpagehide",
|
||||
"onpageshow",
|
||||
"onpaste",
|
||||
"onpause",
|
||||
"onplay",
|
||||
"onplaying",
|
||||
"onpopstate",
|
||||
"onprogress",
|
||||
"onratechange",
|
||||
"onreset",
|
||||
"onresize",
|
||||
"onrejectionhandled",
|
||||
"onscroll",
|
||||
"onsecuritypolicyviolation",
|
||||
"onseeked",
|
||||
"onseeking",
|
||||
"onselect",
|
||||
"onshow",
|
||||
"onsort",
|
||||
"onstalled",
|
||||
"onstorage",
|
||||
"onsubmit",
|
||||
"onsuspend",
|
||||
"ontimeupdate",
|
||||
"ontoggle",
|
||||
"onunhandledrejection",
|
||||
"onunload",
|
||||
"onvolumechange",
|
||||
"onwaiting",
|
||||
}
|
||||
|
||||
// extra are ad-hoc values not covered by any of the lists above.
|
||||
var extra = []string{
|
||||
"align",
|
||||
"annotation",
|
||||
"annotation-xml",
|
||||
"applet",
|
||||
"basefont",
|
||||
"bgsound",
|
||||
"big",
|
||||
"blink",
|
||||
"center",
|
||||
"color",
|
||||
"desc",
|
||||
"face",
|
||||
"font",
|
||||
"foreignObject", // HTML is case-insensitive, but SVG-embedded-in-HTML is case-sensitive.
|
||||
"foreignobject",
|
||||
"frame",
|
||||
"frameset",
|
||||
"image",
|
||||
"isindex",
|
||||
"listing",
|
||||
"malignmark",
|
||||
"marquee",
|
||||
"math",
|
||||
"mglyph",
|
||||
"mi",
|
||||
"mn",
|
||||
"mo",
|
||||
"ms",
|
||||
"mtext",
|
||||
"nobr",
|
||||
"noembed",
|
||||
"noframes",
|
||||
"plaintext",
|
||||
"prompt",
|
||||
"public",
|
||||
"spacer",
|
||||
"strike",
|
||||
"svg",
|
||||
"system",
|
||||
"tt",
|
||||
"xmp",
|
||||
}
|
||||
777
vendor/golang.org/x/net/html/atom/table.go
generated
vendored
Normal file
777
vendor/golang.org/x/net/html/atom/table.go
generated
vendored
Normal file
|
|
@ -0,0 +1,777 @@
|
|||
// Code generated by go generate gen.go; DO NOT EDIT.
|
||||
|
||||
//go:generate go run gen.go
|
||||
|
||||
package atom
|
||||
|
||||
const (
|
||||
A Atom = 0x1
|
||||
Abbr Atom = 0x4
|
||||
Accept Atom = 0x1a06
|
||||
AcceptCharset Atom = 0x1a0e
|
||||
Accesskey Atom = 0x2c09
|
||||
Action Atom = 0x25a06
|
||||
Address Atom = 0x6ed07
|
||||
Align Atom = 0x6d405
|
||||
Allowfullscreen Atom = 0x1f00f
|
||||
Allowpaymentrequest Atom = 0x6913
|
||||
Allowusermedia Atom = 0x850e
|
||||
Alt Atom = 0xb003
|
||||
Annotation Atom = 0x1b90a
|
||||
AnnotationXml Atom = 0x1b90e
|
||||
Applet Atom = 0x30106
|
||||
Area Atom = 0x34a04
|
||||
Article Atom = 0x3f007
|
||||
As Atom = 0xb902
|
||||
Aside Atom = 0xc105
|
||||
Async Atom = 0xb905
|
||||
Audio Atom = 0xcf05
|
||||
Autocomplete Atom = 0x2600c
|
||||
Autofocus Atom = 0xeb09
|
||||
Autoplay Atom = 0x10608
|
||||
B Atom = 0x101
|
||||
Base Atom = 0x11504
|
||||
Basefont Atom = 0x11508
|
||||
Bdi Atom = 0x16103
|
||||
Bdo Atom = 0x13403
|
||||
Bgsound Atom = 0x14707
|
||||
Big Atom = 0x15903
|
||||
Blink Atom = 0x15c05
|
||||
Blockquote Atom = 0x1680a
|
||||
Body Atom = 0x2804
|
||||
Br Atom = 0x202
|
||||
Button Atom = 0x17206
|
||||
Canvas Atom = 0xbd06
|
||||
Caption Atom = 0x21907
|
||||
Center Atom = 0x20806
|
||||
Challenge Atom = 0x28309
|
||||
Charset Atom = 0x2107
|
||||
Checked Atom = 0x46d07
|
||||
Cite Atom = 0x55804
|
||||
Class Atom = 0x5b905
|
||||
Code Atom = 0x19004
|
||||
Col Atom = 0x19703
|
||||
Colgroup Atom = 0x19708
|
||||
Color Atom = 0x1af05
|
||||
Cols Atom = 0x1b404
|
||||
Colspan Atom = 0x1b407
|
||||
Command Atom = 0x1c707
|
||||
Content Atom = 0x57f07
|
||||
Contenteditable Atom = 0x57f0f
|
||||
Contextmenu Atom = 0x3740b
|
||||
Controls Atom = 0x1ce08
|
||||
Coords Atom = 0x1da06
|
||||
Crossorigin Atom = 0x1e30b
|
||||
Data Atom = 0x49904
|
||||
Datalist Atom = 0x49908
|
||||
Datetime Atom = 0x2a008
|
||||
Dd Atom = 0x2bf02
|
||||
Default Atom = 0xc407
|
||||
Defer Atom = 0x19205
|
||||
Del Atom = 0x44603
|
||||
Desc Atom = 0x55504
|
||||
Details Atom = 0x4607
|
||||
Dfn Atom = 0x5f03
|
||||
Dialog Atom = 0x16206
|
||||
Dir Atom = 0xa303
|
||||
Dirname Atom = 0xa307
|
||||
Disabled Atom = 0x14d08
|
||||
Div Atom = 0x15403
|
||||
Dl Atom = 0x5e202
|
||||
Download Atom = 0x45708
|
||||
Draggable Atom = 0x18309
|
||||
Dropzone Atom = 0x3f908
|
||||
Dt Atom = 0x64702
|
||||
Em Atom = 0x4202
|
||||
Embed Atom = 0x4205
|
||||
Enctype Atom = 0x27507
|
||||
Face Atom = 0x20604
|
||||
Fieldset Atom = 0x20e08
|
||||
Figcaption Atom = 0x2160a
|
||||
Figure Atom = 0x23006
|
||||
Font Atom = 0x11904
|
||||
Footer Atom = 0xb306
|
||||
For Atom = 0x23c03
|
||||
ForeignObject Atom = 0x23c0d
|
||||
Foreignobject Atom = 0x2490d
|
||||
Form Atom = 0x25604
|
||||
Formaction Atom = 0x2560a
|
||||
Formenctype Atom = 0x2710b
|
||||
Formmethod Atom = 0x28c0a
|
||||
Formnovalidate Atom = 0x2960e
|
||||
Formtarget Atom = 0x2a80a
|
||||
Frame Atom = 0x5705
|
||||
Frameset Atom = 0x5708
|
||||
H1 Atom = 0x14502
|
||||
H2 Atom = 0x2c602
|
||||
H3 Atom = 0x2f502
|
||||
H4 Atom = 0x33902
|
||||
H5 Atom = 0x34302
|
||||
H6 Atom = 0x64902
|
||||
Head Atom = 0x32504
|
||||
Header Atom = 0x32506
|
||||
Headers Atom = 0x32507
|
||||
Height Atom = 0x12c06
|
||||
Hgroup Atom = 0x2b206
|
||||
Hidden Atom = 0x2bd06
|
||||
High Atom = 0x2c304
|
||||
Hr Atom = 0x14002
|
||||
Href Atom = 0x2c804
|
||||
Hreflang Atom = 0x2c808
|
||||
Html Atom = 0x13004
|
||||
HttpEquiv Atom = 0x2d00a
|
||||
I Atom = 0x601
|
||||
Icon Atom = 0x57e04
|
||||
Id Atom = 0xc302
|
||||
Iframe Atom = 0x2e406
|
||||
Image Atom = 0x2ea05
|
||||
Img Atom = 0x2ef03
|
||||
Input Atom = 0x43f05
|
||||
Inputmode Atom = 0x43f09
|
||||
Ins Atom = 0x1ec03
|
||||
Integrity Atom = 0x22709
|
||||
Is Atom = 0x14e02
|
||||
Isindex Atom = 0x2f707
|
||||
Ismap Atom = 0x2fe05
|
||||
Itemid Atom = 0x37f06
|
||||
Itemprop Atom = 0x55908
|
||||
Itemref Atom = 0x3c107
|
||||
Itemscope Atom = 0x66d09
|
||||
Itemtype Atom = 0x30708
|
||||
Kbd Atom = 0x16003
|
||||
Keygen Atom = 0x3206
|
||||
Keytype Atom = 0x7e07
|
||||
Kind Atom = 0x18004
|
||||
Label Atom = 0xda05
|
||||
Lang Atom = 0x2cc04
|
||||
Legend Atom = 0x18a06
|
||||
Li Atom = 0x11102
|
||||
Link Atom = 0x15d04
|
||||
List Atom = 0x49d04
|
||||
Listing Atom = 0x49d07
|
||||
Loop Atom = 0xde04
|
||||
Low Atom = 0x6b03
|
||||
Main Atom = 0x1004
|
||||
Malignmark Atom = 0x6d30a
|
||||
Manifest Atom = 0x30f08
|
||||
Map Atom = 0x30003
|
||||
Mark Atom = 0x6d904
|
||||
Marquee Atom = 0x31b07
|
||||
Math Atom = 0x32204
|
||||
Max Atom = 0x33103
|
||||
Maxlength Atom = 0x33109
|
||||
Media Atom = 0x8e05
|
||||
Mediagroup Atom = 0x8e0a
|
||||
Menu Atom = 0x37b04
|
||||
Menuitem Atom = 0x37b08
|
||||
Meta Atom = 0x4ac04
|
||||
Meter Atom = 0xa805
|
||||
Method Atom = 0x29006
|
||||
Mglyph Atom = 0x2f006
|
||||
Mi Atom = 0x33b02
|
||||
Min Atom = 0x33b03
|
||||
Minlength Atom = 0x33b09
|
||||
Mn Atom = 0x29902
|
||||
Mo Atom = 0x6302
|
||||
Ms Atom = 0x67002
|
||||
Mtext Atom = 0x34505
|
||||
Multiple Atom = 0x35308
|
||||
Muted Atom = 0x35b05
|
||||
Name Atom = 0xa604
|
||||
Nav Atom = 0x1303
|
||||
Nobr Atom = 0x3704
|
||||
Noembed Atom = 0x4007
|
||||
Noframes Atom = 0x5508
|
||||
Nomodule Atom = 0x6108
|
||||
Nonce Atom = 0x56205
|
||||
Noscript Atom = 0x1fe08
|
||||
Novalidate Atom = 0x29a0a
|
||||
Object Atom = 0x25006
|
||||
Ol Atom = 0x10102
|
||||
Onabort Atom = 0x17607
|
||||
Onafterprint Atom = 0x21e0c
|
||||
Onautocomplete Atom = 0x25e0e
|
||||
Onautocompleteerror Atom = 0x25e13
|
||||
Onauxclick Atom = 0x61b0a
|
||||
Onbeforeprint Atom = 0x69a0d
|
||||
Onbeforeunload Atom = 0x6e10e
|
||||
Onblur Atom = 0x5c206
|
||||
Oncancel Atom = 0xd308
|
||||
Oncanplay Atom = 0x13609
|
||||
Oncanplaythrough Atom = 0x13610
|
||||
Onchange Atom = 0x40f08
|
||||
Onclick Atom = 0x2dd07
|
||||
Onclose Atom = 0x36007
|
||||
Oncontextmenu Atom = 0x3720d
|
||||
Oncopy Atom = 0x38506
|
||||
Oncuechange Atom = 0x38b0b
|
||||
Oncut Atom = 0x39605
|
||||
Ondblclick Atom = 0x39b0a
|
||||
Ondrag Atom = 0x3a506
|
||||
Ondragend Atom = 0x3a509
|
||||
Ondragenter Atom = 0x3ae0b
|
||||
Ondragexit Atom = 0x3b90a
|
||||
Ondragleave Atom = 0x3d30b
|
||||
Ondragover Atom = 0x3de0a
|
||||
Ondragstart Atom = 0x3e80b
|
||||
Ondrop Atom = 0x3f706
|
||||
Ondurationchange Atom = 0x40710
|
||||
Onemptied Atom = 0x3fe09
|
||||
Onended Atom = 0x41707
|
||||
Onerror Atom = 0x41e07
|
||||
Onfocus Atom = 0x42507
|
||||
Onhashchange Atom = 0x4310c
|
||||
Oninput Atom = 0x43d07
|
||||
Oninvalid Atom = 0x44909
|
||||
Onkeydown Atom = 0x45209
|
||||
Onkeypress Atom = 0x45f0a
|
||||
Onkeyup Atom = 0x47407
|
||||
Onlanguagechange Atom = 0x48110
|
||||
Onload Atom = 0x49106
|
||||
Onloadeddata Atom = 0x4910c
|
||||
Onloadedmetadata Atom = 0x4a410
|
||||
Onloadend Atom = 0x4ba09
|
||||
Onloadstart Atom = 0x4c30b
|
||||
Onmessage Atom = 0x4ce09
|
||||
Onmessageerror Atom = 0x4ce0e
|
||||
Onmousedown Atom = 0x4dc0b
|
||||
Onmouseenter Atom = 0x4e70c
|
||||
Onmouseleave Atom = 0x4f30c
|
||||
Onmousemove Atom = 0x4ff0b
|
||||
Onmouseout Atom = 0x50a0a
|
||||
Onmouseover Atom = 0x5170b
|
||||
Onmouseup Atom = 0x52209
|
||||
Onmousewheel Atom = 0x5300c
|
||||
Onoffline Atom = 0x53c09
|
||||
Ononline Atom = 0x54508
|
||||
Onpagehide Atom = 0x54d0a
|
||||
Onpageshow Atom = 0x5670a
|
||||
Onpaste Atom = 0x57307
|
||||
Onpause Atom = 0x58e07
|
||||
Onplay Atom = 0x59806
|
||||
Onplaying Atom = 0x59809
|
||||
Onpopstate Atom = 0x5a10a
|
||||
Onprogress Atom = 0x5ab0a
|
||||
Onratechange Atom = 0x5c80c
|
||||
Onrejectionhandled Atom = 0x5d412
|
||||
Onreset Atom = 0x5e607
|
||||
Onresize Atom = 0x5ed08
|
||||
Onscroll Atom = 0x5fc08
|
||||
Onsecuritypolicyviolation Atom = 0x60419
|
||||
Onseeked Atom = 0x62508
|
||||
Onseeking Atom = 0x62d09
|
||||
Onselect Atom = 0x63608
|
||||
Onshow Atom = 0x64006
|
||||
Onsort Atom = 0x64b06
|
||||
Onstalled Atom = 0x65509
|
||||
Onstorage Atom = 0x65e09
|
||||
Onsubmit Atom = 0x66708
|
||||
Onsuspend Atom = 0x67709
|
||||
Ontimeupdate Atom = 0x11a0c
|
||||
Ontoggle Atom = 0x68008
|
||||
Onunhandledrejection Atom = 0x68814
|
||||
Onunload Atom = 0x6a708
|
||||
Onvolumechange Atom = 0x6af0e
|
||||
Onwaiting Atom = 0x6bd09
|
||||
Onwheel Atom = 0x6c607
|
||||
Open Atom = 0x55f04
|
||||
Optgroup Atom = 0xe008
|
||||
Optimum Atom = 0x6cd07
|
||||
Option Atom = 0x6dd06
|
||||
Output Atom = 0x51106
|
||||
P Atom = 0xc01
|
||||
Param Atom = 0xc05
|
||||
Pattern Atom = 0x4f07
|
||||
Picture Atom = 0x9707
|
||||
Ping Atom = 0xe704
|
||||
Placeholder Atom = 0xfb0b
|
||||
Plaintext Atom = 0x19e09
|
||||
Playsinline Atom = 0x10a0b
|
||||
Poster Atom = 0x2b706
|
||||
Pre Atom = 0x46403
|
||||
Preload Atom = 0x47a07
|
||||
Progress Atom = 0x5ad08
|
||||
Prompt Atom = 0x52a06
|
||||
Public Atom = 0x57a06
|
||||
Q Atom = 0x7701
|
||||
Radiogroup Atom = 0x30a
|
||||
Readonly Atom = 0x34b08
|
||||
Referrerpolicy Atom = 0x3c50e
|
||||
Rel Atom = 0x47b03
|
||||
Required Atom = 0x23408
|
||||
Reversed Atom = 0x9c08
|
||||
Rows Atom = 0x3a04
|
||||
Rowspan Atom = 0x3a07
|
||||
Rp Atom = 0x22402
|
||||
Rt Atom = 0x17b02
|
||||
Ruby Atom = 0xac04
|
||||
S Atom = 0x2501
|
||||
Samp Atom = 0x4c04
|
||||
Sandbox Atom = 0xf307
|
||||
Scope Atom = 0x67105
|
||||
Scoped Atom = 0x67106
|
||||
Script Atom = 0x20006
|
||||
Seamless Atom = 0x36508
|
||||
Section Atom = 0x5bd07
|
||||
Select Atom = 0x63806
|
||||
Selected Atom = 0x63808
|
||||
Shape Atom = 0x1d505
|
||||
Size Atom = 0x5f104
|
||||
Sizes Atom = 0x5f105
|
||||
Slot Atom = 0x1df04
|
||||
Small Atom = 0x1ee05
|
||||
Sortable Atom = 0x64d08
|
||||
Sorted Atom = 0x32b06
|
||||
Source Atom = 0x36c06
|
||||
Spacer Atom = 0x42b06
|
||||
Span Atom = 0x3d04
|
||||
Spellcheck Atom = 0x4680a
|
||||
Src Atom = 0x5b403
|
||||
Srcdoc Atom = 0x5b406
|
||||
Srclang Atom = 0x5f507
|
||||
Srcset Atom = 0x6f306
|
||||
Start Atom = 0x3ee05
|
||||
Step Atom = 0x57704
|
||||
Strike Atom = 0x7a06
|
||||
Strong Atom = 0x31506
|
||||
Style Atom = 0x6f905
|
||||
Sub Atom = 0x66903
|
||||
Summary Atom = 0x6fe07
|
||||
Sup Atom = 0x70503
|
||||
Svg Atom = 0x70803
|
||||
System Atom = 0x70b06
|
||||
Tabindex Atom = 0x4b208
|
||||
Table Atom = 0x58905
|
||||
Target Atom = 0x2ac06
|
||||
Tbody Atom = 0x2705
|
||||
Td Atom = 0x5e02
|
||||
Template Atom = 0x70e08
|
||||
Textarea Atom = 0x34608
|
||||
Tfoot Atom = 0xb205
|
||||
Th Atom = 0x13f02
|
||||
Thead Atom = 0x32405
|
||||
Time Atom = 0x11c04
|
||||
Title Atom = 0xca05
|
||||
Tr Atom = 0x7402
|
||||
Track Atom = 0x17c05
|
||||
Translate Atom = 0x1a609
|
||||
Tt Atom = 0x5102
|
||||
Type Atom = 0x8104
|
||||
Typemustmatch Atom = 0x2780d
|
||||
U Atom = 0xb01
|
||||
Ul Atom = 0x6602
|
||||
Updateviacache Atom = 0x1200e
|
||||
Usemap Atom = 0x59206
|
||||
Value Atom = 0x1505
|
||||
Var Atom = 0x15603
|
||||
Video Atom = 0x2d905
|
||||
Wbr Atom = 0x57003
|
||||
Width Atom = 0x64505
|
||||
Workertype Atom = 0x7160a
|
||||
Wrap Atom = 0x72004
|
||||
Xmp Atom = 0xf903
|
||||
)
|
||||
|
||||
const hash0 = 0x81cdf10e
|
||||
|
||||
const maxAtomLen = 25
|
||||
|
||||
var table = [1 << 9]Atom{
|
||||
0x1: 0x8e0a, // mediagroup
|
||||
0x2: 0x2cc04, // lang
|
||||
0x4: 0x2c09, // accesskey
|
||||
0x5: 0x5708, // frameset
|
||||
0x7: 0x63608, // onselect
|
||||
0x8: 0x70b06, // system
|
||||
0xa: 0x64505, // width
|
||||
0xc: 0x2710b, // formenctype
|
||||
0xd: 0x10102, // ol
|
||||
0xe: 0x38b0b, // oncuechange
|
||||
0x10: 0x13403, // bdo
|
||||
0x11: 0xcf05, // audio
|
||||
0x12: 0x18309, // draggable
|
||||
0x14: 0x2d905, // video
|
||||
0x15: 0x29902, // mn
|
||||
0x16: 0x37b04, // menu
|
||||
0x17: 0x2b706, // poster
|
||||
0x19: 0xb306, // footer
|
||||
0x1a: 0x29006, // method
|
||||
0x1b: 0x2a008, // datetime
|
||||
0x1c: 0x17607, // onabort
|
||||
0x1d: 0x1200e, // updateviacache
|
||||
0x1e: 0xb905, // async
|
||||
0x1f: 0x49106, // onload
|
||||
0x21: 0xd308, // oncancel
|
||||
0x22: 0x62508, // onseeked
|
||||
0x23: 0x2ea05, // image
|
||||
0x24: 0x5d412, // onrejectionhandled
|
||||
0x26: 0x15d04, // link
|
||||
0x27: 0x51106, // output
|
||||
0x28: 0x32504, // head
|
||||
0x29: 0x4f30c, // onmouseleave
|
||||
0x2a: 0x57307, // onpaste
|
||||
0x2b: 0x59809, // onplaying
|
||||
0x2c: 0x1b407, // colspan
|
||||
0x2f: 0x1af05, // color
|
||||
0x30: 0x5f104, // size
|
||||
0x31: 0x2d00a, // http-equiv
|
||||
0x33: 0x601, // i
|
||||
0x34: 0x54d0a, // onpagehide
|
||||
0x35: 0x68814, // onunhandledrejection
|
||||
0x37: 0x41e07, // onerror
|
||||
0x3a: 0x11508, // basefont
|
||||
0x3f: 0x1303, // nav
|
||||
0x40: 0x18004, // kind
|
||||
0x41: 0x34b08, // readonly
|
||||
0x42: 0x2f006, // mglyph
|
||||
0x44: 0x11102, // li
|
||||
0x46: 0x2bd06, // hidden
|
||||
0x47: 0x70803, // svg
|
||||
0x48: 0x57704, // step
|
||||
0x49: 0x22709, // integrity
|
||||
0x4a: 0x57a06, // public
|
||||
0x4c: 0x19703, // col
|
||||
0x4d: 0x1680a, // blockquote
|
||||
0x4e: 0x34302, // h5
|
||||
0x50: 0x5ad08, // progress
|
||||
0x51: 0x5f105, // sizes
|
||||
0x52: 0x33902, // h4
|
||||
0x56: 0x32405, // thead
|
||||
0x57: 0x7e07, // keytype
|
||||
0x58: 0x5ab0a, // onprogress
|
||||
0x59: 0x43f09, // inputmode
|
||||
0x5a: 0x3a509, // ondragend
|
||||
0x5d: 0x39605, // oncut
|
||||
0x5e: 0x42b06, // spacer
|
||||
0x5f: 0x19708, // colgroup
|
||||
0x62: 0x14e02, // is
|
||||
0x65: 0xb902, // as
|
||||
0x66: 0x53c09, // onoffline
|
||||
0x67: 0x32b06, // sorted
|
||||
0x69: 0x48110, // onlanguagechange
|
||||
0x6c: 0x4310c, // onhashchange
|
||||
0x6d: 0xa604, // name
|
||||
0x6e: 0xb205, // tfoot
|
||||
0x6f: 0x55504, // desc
|
||||
0x70: 0x33103, // max
|
||||
0x72: 0x1da06, // coords
|
||||
0x73: 0x2f502, // h3
|
||||
0x74: 0x6e10e, // onbeforeunload
|
||||
0x75: 0x3a04, // rows
|
||||
0x76: 0x63806, // select
|
||||
0x77: 0xa805, // meter
|
||||
0x78: 0x37f06, // itemid
|
||||
0x79: 0x5300c, // onmousewheel
|
||||
0x7a: 0x5b406, // srcdoc
|
||||
0x7d: 0x17c05, // track
|
||||
0x7f: 0x30708, // itemtype
|
||||
0x82: 0x6302, // mo
|
||||
0x83: 0x40f08, // onchange
|
||||
0x84: 0x32507, // headers
|
||||
0x85: 0x5c80c, // onratechange
|
||||
0x86: 0x60419, // onsecuritypolicyviolation
|
||||
0x88: 0x49908, // datalist
|
||||
0x89: 0x4dc0b, // onmousedown
|
||||
0x8a: 0x1df04, // slot
|
||||
0x8b: 0x4a410, // onloadedmetadata
|
||||
0x8c: 0x1a06, // accept
|
||||
0x8d: 0x25006, // object
|
||||
0x91: 0x6af0e, // onvolumechange
|
||||
0x92: 0x2107, // charset
|
||||
0x93: 0x25e13, // onautocompleteerror
|
||||
0x94: 0x6913, // allowpaymentrequest
|
||||
0x95: 0x2804, // body
|
||||
0x96: 0xc407, // default
|
||||
0x97: 0x63808, // selected
|
||||
0x98: 0x20604, // face
|
||||
0x99: 0x1d505, // shape
|
||||
0x9b: 0x68008, // ontoggle
|
||||
0x9e: 0x64702, // dt
|
||||
0x9f: 0x6d904, // mark
|
||||
0xa1: 0xb01, // u
|
||||
0xa4: 0x6a708, // onunload
|
||||
0xa5: 0xde04, // loop
|
||||
0xa6: 0x14d08, // disabled
|
||||
0xaa: 0x41707, // onended
|
||||
0xab: 0x6d30a, // malignmark
|
||||
0xad: 0x67709, // onsuspend
|
||||
0xae: 0x34505, // mtext
|
||||
0xaf: 0x64b06, // onsort
|
||||
0xb0: 0x55908, // itemprop
|
||||
0xb3: 0x66d09, // itemscope
|
||||
0xb4: 0x15c05, // blink
|
||||
0xb6: 0x3a506, // ondrag
|
||||
0xb7: 0x6602, // ul
|
||||
0xb8: 0x25604, // form
|
||||
0xb9: 0xf307, // sandbox
|
||||
0xba: 0x5705, // frame
|
||||
0xbb: 0x1505, // value
|
||||
0xbc: 0x65e09, // onstorage
|
||||
0xc0: 0x17b02, // rt
|
||||
0xc2: 0x202, // br
|
||||
0xc3: 0x20e08, // fieldset
|
||||
0xc4: 0x2780d, // typemustmatch
|
||||
0xc5: 0x6108, // nomodule
|
||||
0xc6: 0x4007, // noembed
|
||||
0xc7: 0x69a0d, // onbeforeprint
|
||||
0xc8: 0x17206, // button
|
||||
0xc9: 0x2dd07, // onclick
|
||||
0xca: 0x6fe07, // summary
|
||||
0xcd: 0xac04, // ruby
|
||||
0xce: 0x5b905, // class
|
||||
0xcf: 0x3e80b, // ondragstart
|
||||
0xd0: 0x21907, // caption
|
||||
0xd4: 0x850e, // allowusermedia
|
||||
0xd5: 0x4c30b, // onloadstart
|
||||
0xd9: 0x15403, // div
|
||||
0xda: 0x49d04, // list
|
||||
0xdb: 0x32204, // math
|
||||
0xdc: 0x43f05, // input
|
||||
0xdf: 0x3de0a, // ondragover
|
||||
0xe0: 0x2c602, // h2
|
||||
0xe2: 0x19e09, // plaintext
|
||||
0xe4: 0x4e70c, // onmouseenter
|
||||
0xe7: 0x46d07, // checked
|
||||
0xe8: 0x46403, // pre
|
||||
0xea: 0x35308, // multiple
|
||||
0xeb: 0x16103, // bdi
|
||||
0xec: 0x33109, // maxlength
|
||||
0xed: 0x7701, // q
|
||||
0xee: 0x61b0a, // onauxclick
|
||||
0xf0: 0x57003, // wbr
|
||||
0xf2: 0x11504, // base
|
||||
0xf3: 0x6dd06, // option
|
||||
0xf5: 0x40710, // ondurationchange
|
||||
0xf7: 0x5508, // noframes
|
||||
0xf9: 0x3f908, // dropzone
|
||||
0xfb: 0x67105, // scope
|
||||
0xfc: 0x9c08, // reversed
|
||||
0xfd: 0x3ae0b, // ondragenter
|
||||
0xfe: 0x3ee05, // start
|
||||
0xff: 0xf903, // xmp
|
||||
0x100: 0x5f507, // srclang
|
||||
0x101: 0x2ef03, // img
|
||||
0x104: 0x101, // b
|
||||
0x105: 0x23c03, // for
|
||||
0x106: 0xc105, // aside
|
||||
0x107: 0x43d07, // oninput
|
||||
0x108: 0x34a04, // area
|
||||
0x109: 0x28c0a, // formmethod
|
||||
0x10a: 0x72004, // wrap
|
||||
0x10c: 0x22402, // rp
|
||||
0x10d: 0x45f0a, // onkeypress
|
||||
0x10e: 0x5102, // tt
|
||||
0x110: 0x33b02, // mi
|
||||
0x111: 0x35b05, // muted
|
||||
0x112: 0xb003, // alt
|
||||
0x113: 0x19004, // code
|
||||
0x114: 0x4202, // em
|
||||
0x115: 0x3b90a, // ondragexit
|
||||
0x117: 0x3d04, // span
|
||||
0x119: 0x30f08, // manifest
|
||||
0x11a: 0x37b08, // menuitem
|
||||
0x11b: 0x57f07, // content
|
||||
0x11d: 0x6bd09, // onwaiting
|
||||
0x11f: 0x4ba09, // onloadend
|
||||
0x121: 0x3720d, // oncontextmenu
|
||||
0x123: 0x5c206, // onblur
|
||||
0x124: 0x3f007, // article
|
||||
0x125: 0xa303, // dir
|
||||
0x126: 0xe704, // ping
|
||||
0x127: 0x23408, // required
|
||||
0x128: 0x44909, // oninvalid
|
||||
0x129: 0x6d405, // align
|
||||
0x12b: 0x57e04, // icon
|
||||
0x12c: 0x64902, // h6
|
||||
0x12d: 0x1b404, // cols
|
||||
0x12e: 0x2160a, // figcaption
|
||||
0x12f: 0x45209, // onkeydown
|
||||
0x130: 0x66708, // onsubmit
|
||||
0x131: 0x13609, // oncanplay
|
||||
0x132: 0x70503, // sup
|
||||
0x133: 0xc01, // p
|
||||
0x135: 0x3fe09, // onemptied
|
||||
0x136: 0x38506, // oncopy
|
||||
0x137: 0x55804, // cite
|
||||
0x138: 0x39b0a, // ondblclick
|
||||
0x13a: 0x4ff0b, // onmousemove
|
||||
0x13c: 0x66903, // sub
|
||||
0x13d: 0x47b03, // rel
|
||||
0x13e: 0xe008, // optgroup
|
||||
0x142: 0x3a07, // rowspan
|
||||
0x143: 0x36c06, // source
|
||||
0x144: 0x1fe08, // noscript
|
||||
0x145: 0x55f04, // open
|
||||
0x146: 0x1ec03, // ins
|
||||
0x147: 0x23c0d, // foreignObject
|
||||
0x148: 0x5a10a, // onpopstate
|
||||
0x14a: 0x27507, // enctype
|
||||
0x14b: 0x25e0e, // onautocomplete
|
||||
0x14c: 0x34608, // textarea
|
||||
0x14e: 0x2600c, // autocomplete
|
||||
0x14f: 0x14002, // hr
|
||||
0x150: 0x1ce08, // controls
|
||||
0x151: 0xc302, // id
|
||||
0x153: 0x21e0c, // onafterprint
|
||||
0x155: 0x2490d, // foreignobject
|
||||
0x156: 0x31b07, // marquee
|
||||
0x157: 0x58e07, // onpause
|
||||
0x158: 0x5e202, // dl
|
||||
0x159: 0x12c06, // height
|
||||
0x15a: 0x33b03, // min
|
||||
0x15b: 0xa307, // dirname
|
||||
0x15c: 0x1a609, // translate
|
||||
0x15d: 0x13004, // html
|
||||
0x15e: 0x33b09, // minlength
|
||||
0x15f: 0x47a07, // preload
|
||||
0x160: 0x70e08, // template
|
||||
0x161: 0x3d30b, // ondragleave
|
||||
0x164: 0x5b403, // src
|
||||
0x165: 0x31506, // strong
|
||||
0x167: 0x4c04, // samp
|
||||
0x168: 0x6ed07, // address
|
||||
0x169: 0x54508, // ononline
|
||||
0x16b: 0xfb0b, // placeholder
|
||||
0x16c: 0x2ac06, // target
|
||||
0x16d: 0x1ee05, // small
|
||||
0x16e: 0x6c607, // onwheel
|
||||
0x16f: 0x1b90a, // annotation
|
||||
0x170: 0x4680a, // spellcheck
|
||||
0x171: 0x4607, // details
|
||||
0x172: 0xbd06, // canvas
|
||||
0x173: 0xeb09, // autofocus
|
||||
0x174: 0xc05, // param
|
||||
0x176: 0x45708, // download
|
||||
0x177: 0x44603, // del
|
||||
0x178: 0x36007, // onclose
|
||||
0x179: 0x16003, // kbd
|
||||
0x17a: 0x30106, // applet
|
||||
0x17b: 0x2c804, // href
|
||||
0x17c: 0x5ed08, // onresize
|
||||
0x17e: 0x4910c, // onloadeddata
|
||||
0x180: 0x7402, // tr
|
||||
0x181: 0x2a80a, // formtarget
|
||||
0x182: 0xca05, // title
|
||||
0x183: 0x6f905, // style
|
||||
0x184: 0x7a06, // strike
|
||||
0x185: 0x59206, // usemap
|
||||
0x186: 0x2e406, // iframe
|
||||
0x187: 0x1004, // main
|
||||
0x189: 0x9707, // picture
|
||||
0x18c: 0x2fe05, // ismap
|
||||
0x18e: 0x49904, // data
|
||||
0x18f: 0xda05, // label
|
||||
0x191: 0x3c50e, // referrerpolicy
|
||||
0x192: 0x13f02, // th
|
||||
0x194: 0x52a06, // prompt
|
||||
0x195: 0x5bd07, // section
|
||||
0x197: 0x6cd07, // optimum
|
||||
0x198: 0x2c304, // high
|
||||
0x199: 0x14502, // h1
|
||||
0x19a: 0x65509, // onstalled
|
||||
0x19b: 0x15603, // var
|
||||
0x19c: 0x11c04, // time
|
||||
0x19e: 0x67002, // ms
|
||||
0x19f: 0x32506, // header
|
||||
0x1a0: 0x4ce09, // onmessage
|
||||
0x1a1: 0x56205, // nonce
|
||||
0x1a2: 0x2560a, // formaction
|
||||
0x1a3: 0x20806, // center
|
||||
0x1a4: 0x3704, // nobr
|
||||
0x1a5: 0x58905, // table
|
||||
0x1a6: 0x49d07, // listing
|
||||
0x1a7: 0x18a06, // legend
|
||||
0x1a9: 0x28309, // challenge
|
||||
0x1aa: 0x23006, // figure
|
||||
0x1ab: 0x8e05, // media
|
||||
0x1ae: 0x8104, // type
|
||||
0x1af: 0x11904, // font
|
||||
0x1b0: 0x4ce0e, // onmessageerror
|
||||
0x1b1: 0x36508, // seamless
|
||||
0x1b2: 0x5f03, // dfn
|
||||
0x1b3: 0x19205, // defer
|
||||
0x1b4: 0x6b03, // low
|
||||
0x1b5: 0x62d09, // onseeking
|
||||
0x1b6: 0x5170b, // onmouseover
|
||||
0x1b7: 0x29a0a, // novalidate
|
||||
0x1b8: 0x7160a, // workertype
|
||||
0x1ba: 0x3c107, // itemref
|
||||
0x1bd: 0x1, // a
|
||||
0x1be: 0x30003, // map
|
||||
0x1bf: 0x11a0c, // ontimeupdate
|
||||
0x1c0: 0x14707, // bgsound
|
||||
0x1c1: 0x3206, // keygen
|
||||
0x1c2: 0x2705, // tbody
|
||||
0x1c5: 0x64006, // onshow
|
||||
0x1c7: 0x2501, // s
|
||||
0x1c8: 0x4f07, // pattern
|
||||
0x1cc: 0x13610, // oncanplaythrough
|
||||
0x1ce: 0x2bf02, // dd
|
||||
0x1cf: 0x6f306, // srcset
|
||||
0x1d0: 0x15903, // big
|
||||
0x1d2: 0x64d08, // sortable
|
||||
0x1d3: 0x47407, // onkeyup
|
||||
0x1d5: 0x59806, // onplay
|
||||
0x1d7: 0x4ac04, // meta
|
||||
0x1d8: 0x3f706, // ondrop
|
||||
0x1da: 0x5fc08, // onscroll
|
||||
0x1db: 0x1e30b, // crossorigin
|
||||
0x1dc: 0x5670a, // onpageshow
|
||||
0x1dd: 0x4, // abbr
|
||||
0x1de: 0x5e02, // td
|
||||
0x1df: 0x57f0f, // contenteditable
|
||||
0x1e0: 0x25a06, // action
|
||||
0x1e1: 0x10a0b, // playsinline
|
||||
0x1e2: 0x42507, // onfocus
|
||||
0x1e3: 0x2c808, // hreflang
|
||||
0x1e5: 0x50a0a, // onmouseout
|
||||
0x1e6: 0x5e607, // onreset
|
||||
0x1e7: 0x10608, // autoplay
|
||||
0x1ea: 0x67106, // scoped
|
||||
0x1ec: 0x30a, // radiogroup
|
||||
0x1ee: 0x3740b, // contextmenu
|
||||
0x1ef: 0x52209, // onmouseup
|
||||
0x1f1: 0x2b206, // hgroup
|
||||
0x1f2: 0x1f00f, // allowfullscreen
|
||||
0x1f3: 0x4b208, // tabindex
|
||||
0x1f6: 0x2f707, // isindex
|
||||
0x1f7: 0x1a0e, // accept-charset
|
||||
0x1f8: 0x2960e, // formnovalidate
|
||||
0x1fb: 0x1b90e, // annotation-xml
|
||||
0x1fc: 0x4205, // embed
|
||||
0x1fd: 0x20006, // script
|
||||
0x1fe: 0x16206, // dialog
|
||||
0x1ff: 0x1c707, // command
|
||||
}
|
||||
|
||||
const atomText = "abbradiogrouparamainavalueaccept-charsetbodyaccesskeygenobro" +
|
||||
"wspanoembedetailsampatternoframesetdfnomoduleallowpaymentreq" +
|
||||
"uestrikeytypeallowusermediagroupictureversedirnameterubyaltf" +
|
||||
"ooterasyncanvasidefaultitleaudioncancelabelooptgroupingautof" +
|
||||
"ocusandboxmplaceholderautoplaysinlinebasefontimeupdateviacac" +
|
||||
"heightmlbdoncanplaythrough1bgsoundisabledivarbigblinkbdialog" +
|
||||
"blockquotebuttonabortrackindraggablegendcodefercolgrouplaint" +
|
||||
"extranslatecolorcolspannotation-xmlcommandcontrolshapecoords" +
|
||||
"lotcrossoriginsmallowfullscreenoscriptfacenterfieldsetfigcap" +
|
||||
"tionafterprintegrityfigurequiredforeignObjectforeignobjectfo" +
|
||||
"rmactionautocompleteerrorformenctypemustmatchallengeformmeth" +
|
||||
"odformnovalidatetimeformtargethgrouposterhiddenhigh2hreflang" +
|
||||
"http-equivideonclickiframeimageimglyph3isindexismappletitemt" +
|
||||
"ypemanifestrongmarqueematheadersortedmaxlength4minlength5mte" +
|
||||
"xtareadonlymultiplemutedoncloseamlessourceoncontextmenuitemi" +
|
||||
"doncopyoncuechangeoncutondblclickondragendondragenterondrage" +
|
||||
"xitemreferrerpolicyondragleaveondragoverondragstarticleondro" +
|
||||
"pzonemptiedondurationchangeonendedonerroronfocuspaceronhashc" +
|
||||
"hangeoninputmodeloninvalidonkeydownloadonkeypresspellchecked" +
|
||||
"onkeyupreloadonlanguagechangeonloadeddatalistingonloadedmeta" +
|
||||
"databindexonloadendonloadstartonmessageerroronmousedownonmou" +
|
||||
"seenteronmouseleaveonmousemoveonmouseoutputonmouseoveronmous" +
|
||||
"eupromptonmousewheelonofflineononlineonpagehidescitempropeno" +
|
||||
"nceonpageshowbronpastepublicontenteditableonpausemaponplayin" +
|
||||
"gonpopstateonprogressrcdoclassectionbluronratechangeonreject" +
|
||||
"ionhandledonresetonresizesrclangonscrollonsecuritypolicyviol" +
|
||||
"ationauxclickonseekedonseekingonselectedonshowidth6onsortabl" +
|
||||
"eonstalledonstorageonsubmitemscopedonsuspendontoggleonunhand" +
|
||||
"ledrejectionbeforeprintonunloadonvolumechangeonwaitingonwhee" +
|
||||
"loptimumalignmarkoptionbeforeunloaddressrcsetstylesummarysup" +
|
||||
"svgsystemplateworkertypewrap"
|
||||
373
vendor/golang.org/x/net/html/atom/table_test.go
generated
vendored
Normal file
373
vendor/golang.org/x/net/html/atom/table_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,373 @@
|
|||
// Code generated by go generate gen.go; DO NOT EDIT.
|
||||
|
||||
//go:generate go run gen.go -test
|
||||
|
||||
package atom
|
||||
|
||||
var testAtomList = []string{
|
||||
"a",
|
||||
"abbr",
|
||||
"accept",
|
||||
"accept-charset",
|
||||
"accesskey",
|
||||
"action",
|
||||
"address",
|
||||
"align",
|
||||
"allowfullscreen",
|
||||
"allowpaymentrequest",
|
||||
"allowusermedia",
|
||||
"alt",
|
||||
"annotation",
|
||||
"annotation-xml",
|
||||
"applet",
|
||||
"area",
|
||||
"article",
|
||||
"as",
|
||||
"aside",
|
||||
"async",
|
||||
"audio",
|
||||
"autocomplete",
|
||||
"autofocus",
|
||||
"autoplay",
|
||||
"b",
|
||||
"base",
|
||||
"basefont",
|
||||
"bdi",
|
||||
"bdo",
|
||||
"bgsound",
|
||||
"big",
|
||||
"blink",
|
||||
"blockquote",
|
||||
"body",
|
||||
"br",
|
||||
"button",
|
||||
"canvas",
|
||||
"caption",
|
||||
"center",
|
||||
"challenge",
|
||||
"charset",
|
||||
"checked",
|
||||
"cite",
|
||||
"class",
|
||||
"code",
|
||||
"col",
|
||||
"colgroup",
|
||||
"color",
|
||||
"cols",
|
||||
"colspan",
|
||||
"command",
|
||||
"content",
|
||||
"contenteditable",
|
||||
"contextmenu",
|
||||
"controls",
|
||||
"coords",
|
||||
"crossorigin",
|
||||
"data",
|
||||
"datalist",
|
||||
"datetime",
|
||||
"dd",
|
||||
"default",
|
||||
"defer",
|
||||
"del",
|
||||
"desc",
|
||||
"details",
|
||||
"dfn",
|
||||
"dialog",
|
||||
"dir",
|
||||
"dirname",
|
||||
"disabled",
|
||||
"div",
|
||||
"dl",
|
||||
"download",
|
||||
"draggable",
|
||||
"dropzone",
|
||||
"dt",
|
||||
"em",
|
||||
"embed",
|
||||
"enctype",
|
||||
"face",
|
||||
"fieldset",
|
||||
"figcaption",
|
||||
"figure",
|
||||
"font",
|
||||
"footer",
|
||||
"for",
|
||||
"foreignObject",
|
||||
"foreignobject",
|
||||
"form",
|
||||
"formaction",
|
||||
"formenctype",
|
||||
"formmethod",
|
||||
"formnovalidate",
|
||||
"formtarget",
|
||||
"frame",
|
||||
"frameset",
|
||||
"h1",
|
||||
"h2",
|
||||
"h3",
|
||||
"h4",
|
||||
"h5",
|
||||
"h6",
|
||||
"head",
|
||||
"header",
|
||||
"headers",
|
||||
"height",
|
||||
"hgroup",
|
||||
"hidden",
|
||||
"high",
|
||||
"hr",
|
||||
"href",
|
||||
"hreflang",
|
||||
"html",
|
||||
"http-equiv",
|
||||
"i",
|
||||
"icon",
|
||||
"id",
|
||||
"iframe",
|
||||
"image",
|
||||
"img",
|
||||
"input",
|
||||
"inputmode",
|
||||
"ins",
|
||||
"integrity",
|
||||
"is",
|
||||
"isindex",
|
||||
"ismap",
|
||||
"itemid",
|
||||
"itemprop",
|
||||
"itemref",
|
||||
"itemscope",
|
||||
"itemtype",
|
||||
"kbd",
|
||||
"keygen",
|
||||
"keytype",
|
||||
"kind",
|
||||
"label",
|
||||
"lang",
|
||||
"legend",
|
||||
"li",
|
||||
"link",
|
||||
"list",
|
||||
"listing",
|
||||
"loop",
|
||||
"low",
|
||||
"main",
|
||||
"malignmark",
|
||||
"manifest",
|
||||
"map",
|
||||
"mark",
|
||||
"marquee",
|
||||
"math",
|
||||
"max",
|
||||
"maxlength",
|
||||
"media",
|
||||
"mediagroup",
|
||||
"menu",
|
||||
"menuitem",
|
||||
"meta",
|
||||
"meter",
|
||||
"method",
|
||||
"mglyph",
|
||||
"mi",
|
||||
"min",
|
||||
"minlength",
|
||||
"mn",
|
||||
"mo",
|
||||
"ms",
|
||||
"mtext",
|
||||
"multiple",
|
||||
"muted",
|
||||
"name",
|
||||
"nav",
|
||||
"nobr",
|
||||
"noembed",
|
||||
"noframes",
|
||||
"nomodule",
|
||||
"nonce",
|
||||
"noscript",
|
||||
"novalidate",
|
||||
"object",
|
||||
"ol",
|
||||
"onabort",
|
||||
"onafterprint",
|
||||
"onautocomplete",
|
||||
"onautocompleteerror",
|
||||
"onauxclick",
|
||||
"onbeforeprint",
|
||||
"onbeforeunload",
|
||||
"onblur",
|
||||
"oncancel",
|
||||
"oncanplay",
|
||||
"oncanplaythrough",
|
||||
"onchange",
|
||||
"onclick",
|
||||
"onclose",
|
||||
"oncontextmenu",
|
||||
"oncopy",
|
||||
"oncuechange",
|
||||
"oncut",
|
||||
"ondblclick",
|
||||
"ondrag",
|
||||
"ondragend",
|
||||
"ondragenter",
|
||||
"ondragexit",
|
||||
"ondragleave",
|
||||
"ondragover",
|
||||
"ondragstart",
|
||||
"ondrop",
|
||||
"ondurationchange",
|
||||
"onemptied",
|
||||
"onended",
|
||||
"onerror",
|
||||
"onfocus",
|
||||
"onhashchange",
|
||||
"oninput",
|
||||
"oninvalid",
|
||||
"onkeydown",
|
||||
"onkeypress",
|
||||
"onkeyup",
|
||||
"onlanguagechange",
|
||||
"onload",
|
||||
"onloadeddata",
|
||||
"onloadedmetadata",
|
||||
"onloadend",
|
||||
"onloadstart",
|
||||
"onmessage",
|
||||
"onmessageerror",
|
||||
"onmousedown",
|
||||
"onmouseenter",
|
||||
"onmouseleave",
|
||||
"onmousemove",
|
||||
"onmouseout",
|
||||
"onmouseover",
|
||||
"onmouseup",
|
||||
"onmousewheel",
|
||||
"onoffline",
|
||||
"ononline",
|
||||
"onpagehide",
|
||||
"onpageshow",
|
||||
"onpaste",
|
||||
"onpause",
|
||||
"onplay",
|
||||
"onplaying",
|
||||
"onpopstate",
|
||||
"onprogress",
|
||||
"onratechange",
|
||||
"onrejectionhandled",
|
||||
"onreset",
|
||||
"onresize",
|
||||
"onscroll",
|
||||
"onsecuritypolicyviolation",
|
||||
"onseeked",
|
||||
"onseeking",
|
||||
"onselect",
|
||||
"onshow",
|
||||
"onsort",
|
||||
"onstalled",
|
||||
"onstorage",
|
||||
"onsubmit",
|
||||
"onsuspend",
|
||||
"ontimeupdate",
|
||||
"ontoggle",
|
||||
"onunhandledrejection",
|
||||
"onunload",
|
||||
"onvolumechange",
|
||||
"onwaiting",
|
||||
"onwheel",
|
||||
"open",
|
||||
"optgroup",
|
||||
"optimum",
|
||||
"option",
|
||||
"output",
|
||||
"p",
|
||||
"param",
|
||||
"pattern",
|
||||
"picture",
|
||||
"ping",
|
||||
"placeholder",
|
||||
"plaintext",
|
||||
"playsinline",
|
||||
"poster",
|
||||
"pre",
|
||||
"preload",
|
||||
"progress",
|
||||
"prompt",
|
||||
"public",
|
||||
"q",
|
||||
"radiogroup",
|
||||
"readonly",
|
||||
"referrerpolicy",
|
||||
"rel",
|
||||
"required",
|
||||
"reversed",
|
||||
"rows",
|
||||
"rowspan",
|
||||
"rp",
|
||||
"rt",
|
||||
"ruby",
|
||||
"s",
|
||||
"samp",
|
||||
"sandbox",
|
||||
"scope",
|
||||
"scoped",
|
||||
"script",
|
||||
"seamless",
|
||||
"section",
|
||||
"select",
|
||||
"selected",
|
||||
"shape",
|
||||
"size",
|
||||
"sizes",
|
||||
"slot",
|
||||
"small",
|
||||
"sortable",
|
||||
"sorted",
|
||||
"source",
|
||||
"spacer",
|
||||
"span",
|
||||
"spellcheck",
|
||||
"src",
|
||||
"srcdoc",
|
||||
"srclang",
|
||||
"srcset",
|
||||
"start",
|
||||
"step",
|
||||
"strike",
|
||||
"strong",
|
||||
"style",
|
||||
"sub",
|
||||
"summary",
|
||||
"sup",
|
||||
"svg",
|
||||
"system",
|
||||
"tabindex",
|
||||
"table",
|
||||
"target",
|
||||
"tbody",
|
||||
"td",
|
||||
"template",
|
||||
"textarea",
|
||||
"tfoot",
|
||||
"th",
|
||||
"thead",
|
||||
"time",
|
||||
"title",
|
||||
"tr",
|
||||
"track",
|
||||
"translate",
|
||||
"tt",
|
||||
"type",
|
||||
"typemustmatch",
|
||||
"u",
|
||||
"ul",
|
||||
"updateviacache",
|
||||
"usemap",
|
||||
"value",
|
||||
"var",
|
||||
"video",
|
||||
"wbr",
|
||||
"width",
|
||||
"workertype",
|
||||
"wrap",
|
||||
"xmp",
|
||||
}
|
||||
257
vendor/golang.org/x/net/html/charset/charset.go
generated
vendored
Normal file
257
vendor/golang.org/x/net/html/charset/charset.go
generated
vendored
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package charset provides common text encodings for HTML documents.
|
||||
//
|
||||
// The mapping from encoding labels to encodings is defined at
|
||||
// https://encoding.spec.whatwg.org/.
|
||||
package charset // import "golang.org/x/net/html/charset"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
"golang.org/x/text/encoding"
|
||||
"golang.org/x/text/encoding/charmap"
|
||||
"golang.org/x/text/encoding/htmlindex"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// Lookup returns the encoding with the specified label, and its canonical
|
||||
// name. It returns nil and the empty string if label is not one of the
|
||||
// standard encodings for HTML. Matching is case-insensitive and ignores
|
||||
// leading and trailing whitespace. Encoders will use HTML escape sequences for
|
||||
// runes that are not supported by the character set.
|
||||
func Lookup(label string) (e encoding.Encoding, name string) {
|
||||
e, err := htmlindex.Get(label)
|
||||
if err != nil {
|
||||
return nil, ""
|
||||
}
|
||||
name, _ = htmlindex.Name(e)
|
||||
return &htmlEncoding{e}, name
|
||||
}
|
||||
|
||||
type htmlEncoding struct{ encoding.Encoding }
|
||||
|
||||
func (h *htmlEncoding) NewEncoder() *encoding.Encoder {
|
||||
// HTML requires a non-terminating legacy encoder. We use HTML escapes to
|
||||
// substitute unsupported code points.
|
||||
return encoding.HTMLEscapeUnsupported(h.Encoding.NewEncoder())
|
||||
}
|
||||
|
||||
// DetermineEncoding determines the encoding of an HTML document by examining
|
||||
// up to the first 1024 bytes of content and the declared Content-Type.
|
||||
//
|
||||
// See http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#determining-the-character-encoding
|
||||
func DetermineEncoding(content []byte, contentType string) (e encoding.Encoding, name string, certain bool) {
|
||||
if len(content) > 1024 {
|
||||
content = content[:1024]
|
||||
}
|
||||
|
||||
for _, b := range boms {
|
||||
if bytes.HasPrefix(content, b.bom) {
|
||||
e, name = Lookup(b.enc)
|
||||
return e, name, true
|
||||
}
|
||||
}
|
||||
|
||||
if _, params, err := mime.ParseMediaType(contentType); err == nil {
|
||||
if cs, ok := params["charset"]; ok {
|
||||
if e, name = Lookup(cs); e != nil {
|
||||
return e, name, true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(content) > 0 {
|
||||
e, name = prescan(content)
|
||||
if e != nil {
|
||||
return e, name, false
|
||||
}
|
||||
}
|
||||
|
||||
// Try to detect UTF-8.
|
||||
// First eliminate any partial rune at the end.
|
||||
for i := len(content) - 1; i >= 0 && i > len(content)-4; i-- {
|
||||
b := content[i]
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
if utf8.RuneStart(b) {
|
||||
content = content[:i]
|
||||
break
|
||||
}
|
||||
}
|
||||
hasHighBit := false
|
||||
for _, c := range content {
|
||||
if c >= 0x80 {
|
||||
hasHighBit = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if hasHighBit && utf8.Valid(content) {
|
||||
return encoding.Nop, "utf-8", false
|
||||
}
|
||||
|
||||
// TODO: change default depending on user's locale?
|
||||
return charmap.Windows1252, "windows-1252", false
|
||||
}
|
||||
|
||||
// NewReader returns an io.Reader that converts the content of r to UTF-8.
|
||||
// It calls DetermineEncoding to find out what r's encoding is.
|
||||
func NewReader(r io.Reader, contentType string) (io.Reader, error) {
|
||||
preview := make([]byte, 1024)
|
||||
n, err := io.ReadFull(r, preview)
|
||||
switch {
|
||||
case err == io.ErrUnexpectedEOF:
|
||||
preview = preview[:n]
|
||||
r = bytes.NewReader(preview)
|
||||
case err != nil:
|
||||
return nil, err
|
||||
default:
|
||||
r = io.MultiReader(bytes.NewReader(preview), r)
|
||||
}
|
||||
|
||||
if e, _, _ := DetermineEncoding(preview, contentType); e != encoding.Nop {
|
||||
r = transform.NewReader(r, e.NewDecoder())
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// NewReaderLabel returns a reader that converts from the specified charset to
|
||||
// UTF-8. It uses Lookup to find the encoding that corresponds to label, and
|
||||
// returns an error if Lookup returns nil. It is suitable for use as
|
||||
// encoding/xml.Decoder's CharsetReader function.
|
||||
func NewReaderLabel(label string, input io.Reader) (io.Reader, error) {
|
||||
e, _ := Lookup(label)
|
||||
if e == nil {
|
||||
return nil, fmt.Errorf("unsupported charset: %q", label)
|
||||
}
|
||||
return transform.NewReader(input, e.NewDecoder()), nil
|
||||
}
|
||||
|
||||
func prescan(content []byte) (e encoding.Encoding, name string) {
|
||||
z := html.NewTokenizer(bytes.NewReader(content))
|
||||
for {
|
||||
switch z.Next() {
|
||||
case html.ErrorToken:
|
||||
return nil, ""
|
||||
|
||||
case html.StartTagToken, html.SelfClosingTagToken:
|
||||
tagName, hasAttr := z.TagName()
|
||||
if !bytes.Equal(tagName, []byte("meta")) {
|
||||
continue
|
||||
}
|
||||
attrList := make(map[string]bool)
|
||||
gotPragma := false
|
||||
|
||||
const (
|
||||
dontKnow = iota
|
||||
doNeedPragma
|
||||
doNotNeedPragma
|
||||
)
|
||||
needPragma := dontKnow
|
||||
|
||||
name = ""
|
||||
e = nil
|
||||
for hasAttr {
|
||||
var key, val []byte
|
||||
key, val, hasAttr = z.TagAttr()
|
||||
ks := string(key)
|
||||
if attrList[ks] {
|
||||
continue
|
||||
}
|
||||
attrList[ks] = true
|
||||
for i, c := range val {
|
||||
if 'A' <= c && c <= 'Z' {
|
||||
val[i] = c + 0x20
|
||||
}
|
||||
}
|
||||
|
||||
switch ks {
|
||||
case "http-equiv":
|
||||
if bytes.Equal(val, []byte("content-type")) {
|
||||
gotPragma = true
|
||||
}
|
||||
|
||||
case "content":
|
||||
if e == nil {
|
||||
name = fromMetaElement(string(val))
|
||||
if name != "" {
|
||||
e, name = Lookup(name)
|
||||
if e != nil {
|
||||
needPragma = doNeedPragma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case "charset":
|
||||
e, name = Lookup(string(val))
|
||||
needPragma = doNotNeedPragma
|
||||
}
|
||||
}
|
||||
|
||||
if needPragma == dontKnow || needPragma == doNeedPragma && !gotPragma {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(name, "utf-16") {
|
||||
name = "utf-8"
|
||||
e = encoding.Nop
|
||||
}
|
||||
|
||||
if e != nil {
|
||||
return e, name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fromMetaElement(s string) string {
|
||||
for s != "" {
|
||||
csLoc := strings.Index(s, "charset")
|
||||
if csLoc == -1 {
|
||||
return ""
|
||||
}
|
||||
s = s[csLoc+len("charset"):]
|
||||
s = strings.TrimLeft(s, " \t\n\f\r")
|
||||
if !strings.HasPrefix(s, "=") {
|
||||
continue
|
||||
}
|
||||
s = s[1:]
|
||||
s = strings.TrimLeft(s, " \t\n\f\r")
|
||||
if s == "" {
|
||||
return ""
|
||||
}
|
||||
if q := s[0]; q == '"' || q == '\'' {
|
||||
s = s[1:]
|
||||
closeQuote := strings.IndexRune(s, rune(q))
|
||||
if closeQuote == -1 {
|
||||
return ""
|
||||
}
|
||||
return s[:closeQuote]
|
||||
}
|
||||
|
||||
end := strings.IndexAny(s, "; \t\n\f\r")
|
||||
if end == -1 {
|
||||
end = len(s)
|
||||
}
|
||||
return s[:end]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var boms = []struct {
|
||||
bom []byte
|
||||
enc string
|
||||
}{
|
||||
{[]byte{0xfe, 0xff}, "utf-16be"},
|
||||
{[]byte{0xff, 0xfe}, "utf-16le"},
|
||||
{[]byte{0xef, 0xbb, 0xbf}, "utf-8"},
|
||||
}
|
||||
237
vendor/golang.org/x/net/html/charset/charset_test.go
generated
vendored
Normal file
237
vendor/golang.org/x/net/html/charset/charset_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package charset
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"io/ioutil"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
func transformString(t transform.Transformer, s string) (string, error) {
|
||||
r := transform.NewReader(strings.NewReader(s), t)
|
||||
b, err := ioutil.ReadAll(r)
|
||||
return string(b), err
|
||||
}
|
||||
|
||||
type testCase struct {
|
||||
utf8, other, otherEncoding string
|
||||
}
|
||||
|
||||
// testCases for encoding and decoding.
|
||||
var testCases = []testCase{
|
||||
{"Résumé", "Résumé", "utf8"},
|
||||
{"Résumé", "R\xe9sum\xe9", "latin1"},
|
||||
{"これは漢字です。", "S0\x8c0o0\"oW[g0Y0\x020", "UTF-16LE"},
|
||||
{"これは漢字です。", "0S0\x8c0oo\"[W0g0Y0\x02", "UTF-16BE"},
|
||||
{"Hello, world", "Hello, world", "ASCII"},
|
||||
{"Gdańsk", "Gda\xf1sk", "ISO-8859-2"},
|
||||
{"Ââ Čč Đđ Ŋŋ Õõ Šš Žž Åå Ää", "\xc2\xe2 \xc8\xe8 \xa9\xb9 \xaf\xbf \xd5\xf5 \xaa\xba \xac\xbc \xc5\xe5 \xc4\xe4", "ISO-8859-10"},
|
||||
{"สำหรับ", "\xca\xd3\xcb\xc3\u047a", "ISO-8859-11"},
|
||||
{"latviešu", "latvie\xf0u", "ISO-8859-13"},
|
||||
{"Seònaid", "Se\xf2naid", "ISO-8859-14"},
|
||||
{"€1 is cheap", "\xa41 is cheap", "ISO-8859-15"},
|
||||
{"românește", "rom\xe2ne\xbate", "ISO-8859-16"},
|
||||
{"nutraĵo", "nutra\xbco", "ISO-8859-3"},
|
||||
{"Kalâdlit", "Kal\xe2dlit", "ISO-8859-4"},
|
||||
{"русский", "\xe0\xe3\xe1\xe1\xda\xd8\xd9", "ISO-8859-5"},
|
||||
{"ελληνικά", "\xe5\xeb\xeb\xe7\xed\xe9\xea\xdc", "ISO-8859-7"},
|
||||
{"Kağan", "Ka\xf0an", "ISO-8859-9"},
|
||||
{"Résumé", "R\x8esum\x8e", "macintosh"},
|
||||
{"Gdańsk", "Gda\xf1sk", "windows-1250"},
|
||||
{"русский", "\xf0\xf3\xf1\xf1\xea\xe8\xe9", "windows-1251"},
|
||||
{"Résumé", "R\xe9sum\xe9", "windows-1252"},
|
||||
{"ελληνικά", "\xe5\xeb\xeb\xe7\xed\xe9\xea\xdc", "windows-1253"},
|
||||
{"Kağan", "Ka\xf0an", "windows-1254"},
|
||||
{"עִבְרִית", "\xf2\xc4\xe1\xc0\xf8\xc4\xe9\xfa", "windows-1255"},
|
||||
{"العربية", "\xc7\xe1\xda\xd1\xc8\xed\xc9", "windows-1256"},
|
||||
{"latviešu", "latvie\xf0u", "windows-1257"},
|
||||
{"Việt", "Vi\xea\xf2t", "windows-1258"},
|
||||
{"สำหรับ", "\xca\xd3\xcb\xc3\u047a", "windows-874"},
|
||||
{"русский", "\xd2\xd5\xd3\xd3\xcb\xc9\xca", "KOI8-R"},
|
||||
{"українська", "\xd5\xcb\xd2\xc1\xa7\xce\xd3\xd8\xcb\xc1", "KOI8-U"},
|
||||
{"Hello 常用國字標準字體表", "Hello \xb1`\xa5\u03b0\xea\xa6r\xbc\u0437\u01e6r\xc5\xe9\xaa\xed", "big5"},
|
||||
{"Hello 常用國字標準字體表", "Hello \xb3\xa3\xd3\xc3\x87\xf8\xd7\xd6\x98\xcb\x9c\xca\xd7\xd6\xf3\x77\xb1\xed", "gbk"},
|
||||
{"Hello 常用國字標準字體表", "Hello \xb3\xa3\xd3\xc3\x87\xf8\xd7\xd6\x98\xcb\x9c\xca\xd7\xd6\xf3\x77\xb1\xed", "gb18030"},
|
||||
{"עִבְרִית", "\x81\x30\xfb\x30\x81\x30\xf6\x34\x81\x30\xf9\x33\x81\x30\xf6\x30\x81\x30\xfb\x36\x81\x30\xf6\x34\x81\x30\xfa\x31\x81\x30\xfb\x38", "gb18030"},
|
||||
{"㧯", "\x82\x31\x89\x38", "gb18030"},
|
||||
{"これは漢字です。", "\x82\xb1\x82\xea\x82\xcd\x8a\xbf\x8e\x9a\x82\xc5\x82\xb7\x81B", "SJIS"},
|
||||
{"Hello, 世界!", "Hello, \x90\xa2\x8aE!", "SJIS"},
|
||||
{"イウエオカ", "\xb2\xb3\xb4\xb5\xb6", "SJIS"},
|
||||
{"これは漢字です。", "\xa4\xb3\xa4\xec\xa4\u03f4\xc1\xbb\xfa\xa4\u01e4\xb9\xa1\xa3", "EUC-JP"},
|
||||
{"Hello, 世界!", "Hello, \x1b$B@$3&\x1b(B!", "ISO-2022-JP"},
|
||||
{"다음과 같은 조건을 따라야 합니다: 저작자표시", "\xb4\xd9\xc0\xbd\xb0\xfa \xb0\xb0\xc0\xba \xc1\xb6\xb0\xc7\xc0\xbb \xb5\xfb\xb6\xf3\xbe\xdf \xc7մϴ\xd9: \xc0\xfa\xc0\xdb\xc0\xdaǥ\xbd\xc3", "EUC-KR"},
|
||||
}
|
||||
|
||||
func TestDecode(t *testing.T) {
|
||||
testCases := append(testCases, []testCase{
|
||||
// Replace multi-byte maximum subpart of ill-formed subsequence with
|
||||
// single replacement character (WhatWG requirement).
|
||||
{"Rés\ufffdumé", "Rés\xe1\x80umé", "utf8"},
|
||||
}...)
|
||||
for _, tc := range testCases {
|
||||
e, _ := Lookup(tc.otherEncoding)
|
||||
if e == nil {
|
||||
t.Errorf("%s: not found", tc.otherEncoding)
|
||||
continue
|
||||
}
|
||||
s, err := transformString(e.NewDecoder(), tc.other)
|
||||
if err != nil {
|
||||
t.Errorf("%s: decode %q: %v", tc.otherEncoding, tc.other, err)
|
||||
continue
|
||||
}
|
||||
if s != tc.utf8 {
|
||||
t.Errorf("%s: got %q, want %q", tc.otherEncoding, s, tc.utf8)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncode(t *testing.T) {
|
||||
testCases := append(testCases, []testCase{
|
||||
// Use Go-style replacement.
|
||||
{"Rés\xe1\x80umé", "Rés\ufffd\ufffdumé", "utf8"},
|
||||
// U+0144 LATIN SMALL LETTER N WITH ACUTE not supported by encoding.
|
||||
{"Gdańsk", "Gdańsk", "ISO-8859-11"},
|
||||
{"\ufffd", "�", "ISO-8859-11"},
|
||||
{"a\xe1\x80b", "a��b", "ISO-8859-11"},
|
||||
}...)
|
||||
for _, tc := range testCases {
|
||||
e, _ := Lookup(tc.otherEncoding)
|
||||
if e == nil {
|
||||
t.Errorf("%s: not found", tc.otherEncoding)
|
||||
continue
|
||||
}
|
||||
s, err := transformString(e.NewEncoder(), tc.utf8)
|
||||
if err != nil {
|
||||
t.Errorf("%s: encode %q: %s", tc.otherEncoding, tc.utf8, err)
|
||||
continue
|
||||
}
|
||||
if s != tc.other {
|
||||
t.Errorf("%s: got %q, want %q", tc.otherEncoding, s, tc.other)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var sniffTestCases = []struct {
|
||||
filename, declared, want string
|
||||
}{
|
||||
{"HTTP-charset.html", "text/html; charset=iso-8859-15", "iso-8859-15"},
|
||||
{"UTF-16LE-BOM.html", "", "utf-16le"},
|
||||
{"UTF-16BE-BOM.html", "", "utf-16be"},
|
||||
{"meta-content-attribute.html", "text/html", "iso-8859-15"},
|
||||
{"meta-charset-attribute.html", "text/html", "iso-8859-15"},
|
||||
{"No-encoding-declaration.html", "text/html", "utf-8"},
|
||||
{"HTTP-vs-UTF-8-BOM.html", "text/html; charset=iso-8859-15", "utf-8"},
|
||||
{"HTTP-vs-meta-content.html", "text/html; charset=iso-8859-15", "iso-8859-15"},
|
||||
{"HTTP-vs-meta-charset.html", "text/html; charset=iso-8859-15", "iso-8859-15"},
|
||||
{"UTF-8-BOM-vs-meta-content.html", "text/html", "utf-8"},
|
||||
{"UTF-8-BOM-vs-meta-charset.html", "text/html", "utf-8"},
|
||||
}
|
||||
|
||||
func TestSniff(t *testing.T) {
|
||||
switch runtime.GOOS {
|
||||
case "nacl": // platforms that don't permit direct file system access
|
||||
t.Skipf("not supported on %q", runtime.GOOS)
|
||||
}
|
||||
|
||||
for _, tc := range sniffTestCases {
|
||||
content, err := ioutil.ReadFile("testdata/" + tc.filename)
|
||||
if err != nil {
|
||||
t.Errorf("%s: error reading file: %v", tc.filename, err)
|
||||
continue
|
||||
}
|
||||
|
||||
_, name, _ := DetermineEncoding(content, tc.declared)
|
||||
if name != tc.want {
|
||||
t.Errorf("%s: got %q, want %q", tc.filename, name, tc.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReader(t *testing.T) {
|
||||
switch runtime.GOOS {
|
||||
case "nacl": // platforms that don't permit direct file system access
|
||||
t.Skipf("not supported on %q", runtime.GOOS)
|
||||
}
|
||||
|
||||
for _, tc := range sniffTestCases {
|
||||
content, err := ioutil.ReadFile("testdata/" + tc.filename)
|
||||
if err != nil {
|
||||
t.Errorf("%s: error reading file: %v", tc.filename, err)
|
||||
continue
|
||||
}
|
||||
|
||||
r, err := NewReader(bytes.NewReader(content), tc.declared)
|
||||
if err != nil {
|
||||
t.Errorf("%s: error creating reader: %v", tc.filename, err)
|
||||
continue
|
||||
}
|
||||
|
||||
got, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
t.Errorf("%s: error reading from charset.NewReader: %v", tc.filename, err)
|
||||
continue
|
||||
}
|
||||
|
||||
e, _ := Lookup(tc.want)
|
||||
want, err := ioutil.ReadAll(transform.NewReader(bytes.NewReader(content), e.NewDecoder()))
|
||||
if err != nil {
|
||||
t.Errorf("%s: error decoding with hard-coded charset name: %v", tc.filename, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("%s: got %q, want %q", tc.filename, got, want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var metaTestCases = []struct {
|
||||
meta, want string
|
||||
}{
|
||||
{"", ""},
|
||||
{"text/html", ""},
|
||||
{"text/html; charset utf-8", ""},
|
||||
{"text/html; charset=latin-2", "latin-2"},
|
||||
{"text/html; charset; charset = utf-8", "utf-8"},
|
||||
{`charset="big5"`, "big5"},
|
||||
{"charset='shift_jis'", "shift_jis"},
|
||||
}
|
||||
|
||||
func TestFromMeta(t *testing.T) {
|
||||
for _, tc := range metaTestCases {
|
||||
got := fromMetaElement(tc.meta)
|
||||
if got != tc.want {
|
||||
t.Errorf("%q: got %q, want %q", tc.meta, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestXML(t *testing.T) {
|
||||
const s = "<?xml version=\"1.0\" encoding=\"windows-1252\"?><a><Word>r\xe9sum\xe9</Word></a>"
|
||||
|
||||
d := xml.NewDecoder(strings.NewReader(s))
|
||||
d.CharsetReader = NewReaderLabel
|
||||
|
||||
var a struct {
|
||||
Word string
|
||||
}
|
||||
err := d.Decode(&a)
|
||||
if err != nil {
|
||||
t.Fatalf("Decode: %v", err)
|
||||
}
|
||||
|
||||
want := "résumé"
|
||||
if a.Word != want {
|
||||
t.Errorf("got %q, want %q", a.Word, want)
|
||||
}
|
||||
}
|
||||
104
vendor/golang.org/x/net/html/const.go
generated
vendored
Normal file
104
vendor/golang.org/x/net/html/const.go
generated
vendored
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package html
|
||||
|
||||
// Section 12.2.3.2 of the HTML5 specification says "The following elements
|
||||
// have varying levels of special parsing rules".
|
||||
// https://html.spec.whatwg.org/multipage/syntax.html#the-stack-of-open-elements
|
||||
var isSpecialElementMap = map[string]bool{
|
||||
"address": true,
|
||||
"applet": true,
|
||||
"area": true,
|
||||
"article": true,
|
||||
"aside": true,
|
||||
"base": true,
|
||||
"basefont": true,
|
||||
"bgsound": true,
|
||||
"blockquote": true,
|
||||
"body": true,
|
||||
"br": true,
|
||||
"button": true,
|
||||
"caption": true,
|
||||
"center": true,
|
||||
"col": true,
|
||||
"colgroup": true,
|
||||
"dd": true,
|
||||
"details": true,
|
||||
"dir": true,
|
||||
"div": true,
|
||||
"dl": true,
|
||||
"dt": true,
|
||||
"embed": true,
|
||||
"fieldset": true,
|
||||
"figcaption": true,
|
||||
"figure": true,
|
||||
"footer": true,
|
||||
"form": true,
|
||||
"frame": true,
|
||||
"frameset": true,
|
||||
"h1": true,
|
||||
"h2": true,
|
||||
"h3": true,
|
||||
"h4": true,
|
||||
"h5": true,
|
||||
"h6": true,
|
||||
"head": true,
|
||||
"header": true,
|
||||
"hgroup": true,
|
||||
"hr": true,
|
||||
"html": true,
|
||||
"iframe": true,
|
||||
"img": true,
|
||||
"input": true,
|
||||
"isindex": true, // The 'isindex' element has been removed, but keep it for backwards compatibility.
|
||||
"keygen": true,
|
||||
"li": true,
|
||||
"link": true,
|
||||
"listing": true,
|
||||
"main": true,
|
||||
"marquee": true,
|
||||
"menu": true,
|
||||
"meta": true,
|
||||
"nav": true,
|
||||
"noembed": true,
|
||||
"noframes": true,
|
||||
"noscript": true,
|
||||
"object": true,
|
||||
"ol": true,
|
||||
"p": true,
|
||||
"param": true,
|
||||
"plaintext": true,
|
||||
"pre": true,
|
||||
"script": true,
|
||||
"section": true,
|
||||
"select": true,
|
||||
"source": true,
|
||||
"style": true,
|
||||
"summary": true,
|
||||
"table": true,
|
||||
"tbody": true,
|
||||
"td": true,
|
||||
"template": true,
|
||||
"textarea": true,
|
||||
"tfoot": true,
|
||||
"th": true,
|
||||
"thead": true,
|
||||
"title": true,
|
||||
"tr": true,
|
||||
"track": true,
|
||||
"ul": true,
|
||||
"wbr": true,
|
||||
"xmp": true,
|
||||
}
|
||||
|
||||
func isSpecialElement(element *Node) bool {
|
||||
switch element.Namespace {
|
||||
case "", "html":
|
||||
return isSpecialElementMap[element.Data]
|
||||
case "svg":
|
||||
return element.Data == "foreignObject"
|
||||
}
|
||||
return false
|
||||
}
|
||||
106
vendor/golang.org/x/net/html/doc.go
generated
vendored
Normal file
106
vendor/golang.org/x/net/html/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
// Copyright 2010 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 html implements an HTML5-compliant tokenizer and parser.
|
||||
|
||||
Tokenization is done by creating a Tokenizer for an io.Reader r. It is the
|
||||
caller's responsibility to ensure that r provides UTF-8 encoded HTML.
|
||||
|
||||
z := html.NewTokenizer(r)
|
||||
|
||||
Given a Tokenizer z, the HTML is tokenized by repeatedly calling z.Next(),
|
||||
which parses the next token and returns its type, or an error:
|
||||
|
||||
for {
|
||||
tt := z.Next()
|
||||
if tt == html.ErrorToken {
|
||||
// ...
|
||||
return ...
|
||||
}
|
||||
// Process the current token.
|
||||
}
|
||||
|
||||
There are two APIs for retrieving the current token. The high-level API is to
|
||||
call Token; the low-level API is to call Text or TagName / TagAttr. Both APIs
|
||||
allow optionally calling Raw after Next but before Token, Text, TagName, or
|
||||
TagAttr. In EBNF notation, the valid call sequence per token is:
|
||||
|
||||
Next {Raw} [ Token | Text | TagName {TagAttr} ]
|
||||
|
||||
Token returns an independent data structure that completely describes a token.
|
||||
Entities (such as "<") are unescaped, tag names and attribute keys are
|
||||
lower-cased, and attributes are collected into a []Attribute. For example:
|
||||
|
||||
for {
|
||||
if z.Next() == html.ErrorToken {
|
||||
// Returning io.EOF indicates success.
|
||||
return z.Err()
|
||||
}
|
||||
emitToken(z.Token())
|
||||
}
|
||||
|
||||
The low-level API performs fewer allocations and copies, but the contents of
|
||||
the []byte values returned by Text, TagName and TagAttr may change on the next
|
||||
call to Next. For example, to extract an HTML page's anchor text:
|
||||
|
||||
depth := 0
|
||||
for {
|
||||
tt := z.Next()
|
||||
switch tt {
|
||||
case ErrorToken:
|
||||
return z.Err()
|
||||
case TextToken:
|
||||
if depth > 0 {
|
||||
// emitBytes should copy the []byte it receives,
|
||||
// if it doesn't process it immediately.
|
||||
emitBytes(z.Text())
|
||||
}
|
||||
case StartTagToken, EndTagToken:
|
||||
tn, _ := z.TagName()
|
||||
if len(tn) == 1 && tn[0] == 'a' {
|
||||
if tt == StartTagToken {
|
||||
depth++
|
||||
} else {
|
||||
depth--
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Parsing is done by calling Parse with an io.Reader, which returns the root of
|
||||
the parse tree (the document element) as a *Node. It is the caller's
|
||||
responsibility to ensure that the Reader provides UTF-8 encoded HTML. For
|
||||
example, to process each anchor node in depth-first order:
|
||||
|
||||
doc, err := html.Parse(r)
|
||||
if err != nil {
|
||||
// ...
|
||||
}
|
||||
var f func(*html.Node)
|
||||
f = func(n *html.Node) {
|
||||
if n.Type == html.ElementNode && n.Data == "a" {
|
||||
// Do something with n...
|
||||
}
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
f(c)
|
||||
}
|
||||
}
|
||||
f(doc)
|
||||
|
||||
The relevant specifications include:
|
||||
https://html.spec.whatwg.org/multipage/syntax.html and
|
||||
https://html.spec.whatwg.org/multipage/syntax.html#tokenization
|
||||
*/
|
||||
package html // import "golang.org/x/net/html"
|
||||
|
||||
// The tokenization algorithm implemented by this package is not a line-by-line
|
||||
// transliteration of the relatively verbose state-machine in the WHATWG
|
||||
// specification. A more direct approach is used instead, where the program
|
||||
// counter implies the state, such as whether it is tokenizing a tag or a text
|
||||
// node. Specification compliance is verified by checking expected and actual
|
||||
// outputs over a test suite rather than aiming for algorithmic fidelity.
|
||||
|
||||
// TODO(nigeltao): Does a DOM API belong in this package or a separate one?
|
||||
// TODO(nigeltao): How does parsing interact with a JavaScript engine?
|
||||
156
vendor/golang.org/x/net/html/doctype.go
generated
vendored
Normal file
156
vendor/golang.org/x/net/html/doctype.go
generated
vendored
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package html
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// parseDoctype parses the data from a DoctypeToken into a name,
|
||||
// public identifier, and system identifier. It returns a Node whose Type
|
||||
// is DoctypeNode, whose Data is the name, and which has attributes
|
||||
// named "system" and "public" for the two identifiers if they were present.
|
||||
// quirks is whether the document should be parsed in "quirks mode".
|
||||
func parseDoctype(s string) (n *Node, quirks bool) {
|
||||
n = &Node{Type: DoctypeNode}
|
||||
|
||||
// Find the name.
|
||||
space := strings.IndexAny(s, whitespace)
|
||||
if space == -1 {
|
||||
space = len(s)
|
||||
}
|
||||
n.Data = s[:space]
|
||||
// The comparison to "html" is case-sensitive.
|
||||
if n.Data != "html" {
|
||||
quirks = true
|
||||
}
|
||||
n.Data = strings.ToLower(n.Data)
|
||||
s = strings.TrimLeft(s[space:], whitespace)
|
||||
|
||||
if len(s) < 6 {
|
||||
// It can't start with "PUBLIC" or "SYSTEM".
|
||||
// Ignore the rest of the string.
|
||||
return n, quirks || s != ""
|
||||
}
|
||||
|
||||
key := strings.ToLower(s[:6])
|
||||
s = s[6:]
|
||||
for key == "public" || key == "system" {
|
||||
s = strings.TrimLeft(s, whitespace)
|
||||
if s == "" {
|
||||
break
|
||||
}
|
||||
quote := s[0]
|
||||
if quote != '"' && quote != '\'' {
|
||||
break
|
||||
}
|
||||
s = s[1:]
|
||||
q := strings.IndexRune(s, rune(quote))
|
||||
var id string
|
||||
if q == -1 {
|
||||
id = s
|
||||
s = ""
|
||||
} else {
|
||||
id = s[:q]
|
||||
s = s[q+1:]
|
||||
}
|
||||
n.Attr = append(n.Attr, Attribute{Key: key, Val: id})
|
||||
if key == "public" {
|
||||
key = "system"
|
||||
} else {
|
||||
key = ""
|
||||
}
|
||||
}
|
||||
|
||||
if key != "" || s != "" {
|
||||
quirks = true
|
||||
} else if len(n.Attr) > 0 {
|
||||
if n.Attr[0].Key == "public" {
|
||||
public := strings.ToLower(n.Attr[0].Val)
|
||||
switch public {
|
||||
case "-//w3o//dtd w3 html strict 3.0//en//", "-/w3d/dtd html 4.0 transitional/en", "html":
|
||||
quirks = true
|
||||
default:
|
||||
for _, q := range quirkyIDs {
|
||||
if strings.HasPrefix(public, q) {
|
||||
quirks = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
// The following two public IDs only cause quirks mode if there is no system ID.
|
||||
if len(n.Attr) == 1 && (strings.HasPrefix(public, "-//w3c//dtd html 4.01 frameset//") ||
|
||||
strings.HasPrefix(public, "-//w3c//dtd html 4.01 transitional//")) {
|
||||
quirks = true
|
||||
}
|
||||
}
|
||||
if lastAttr := n.Attr[len(n.Attr)-1]; lastAttr.Key == "system" &&
|
||||
strings.ToLower(lastAttr.Val) == "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd" {
|
||||
quirks = true
|
||||
}
|
||||
}
|
||||
|
||||
return n, quirks
|
||||
}
|
||||
|
||||
// quirkyIDs is a list of public doctype identifiers that cause a document
|
||||
// to be interpreted in quirks mode. The identifiers should be in lower case.
|
||||
var quirkyIDs = []string{
|
||||
"+//silmaril//dtd html pro v0r11 19970101//",
|
||||
"-//advasoft ltd//dtd html 3.0 aswedit + extensions//",
|
||||
"-//as//dtd html 3.0 aswedit + extensions//",
|
||||
"-//ietf//dtd html 2.0 level 1//",
|
||||
"-//ietf//dtd html 2.0 level 2//",
|
||||
"-//ietf//dtd html 2.0 strict level 1//",
|
||||
"-//ietf//dtd html 2.0 strict level 2//",
|
||||
"-//ietf//dtd html 2.0 strict//",
|
||||
"-//ietf//dtd html 2.0//",
|
||||
"-//ietf//dtd html 2.1e//",
|
||||
"-//ietf//dtd html 3.0//",
|
||||
"-//ietf//dtd html 3.2 final//",
|
||||
"-//ietf//dtd html 3.2//",
|
||||
"-//ietf//dtd html 3//",
|
||||
"-//ietf//dtd html level 0//",
|
||||
"-//ietf//dtd html level 1//",
|
||||
"-//ietf//dtd html level 2//",
|
||||
"-//ietf//dtd html level 3//",
|
||||
"-//ietf//dtd html strict level 0//",
|
||||
"-//ietf//dtd html strict level 1//",
|
||||
"-//ietf//dtd html strict level 2//",
|
||||
"-//ietf//dtd html strict level 3//",
|
||||
"-//ietf//dtd html strict//",
|
||||
"-//ietf//dtd html//",
|
||||
"-//metrius//dtd metrius presentational//",
|
||||
"-//microsoft//dtd internet explorer 2.0 html strict//",
|
||||
"-//microsoft//dtd internet explorer 2.0 html//",
|
||||
"-//microsoft//dtd internet explorer 2.0 tables//",
|
||||
"-//microsoft//dtd internet explorer 3.0 html strict//",
|
||||
"-//microsoft//dtd internet explorer 3.0 html//",
|
||||
"-//microsoft//dtd internet explorer 3.0 tables//",
|
||||
"-//netscape comm. corp.//dtd html//",
|
||||
"-//netscape comm. corp.//dtd strict html//",
|
||||
"-//o'reilly and associates//dtd html 2.0//",
|
||||
"-//o'reilly and associates//dtd html extended 1.0//",
|
||||
"-//o'reilly and associates//dtd html extended relaxed 1.0//",
|
||||
"-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//",
|
||||
"-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//",
|
||||
"-//spyglass//dtd html 2.0 extended//",
|
||||
"-//sq//dtd html 2.0 hotmetal + extensions//",
|
||||
"-//sun microsystems corp.//dtd hotjava html//",
|
||||
"-//sun microsystems corp.//dtd hotjava strict html//",
|
||||
"-//w3c//dtd html 3 1995-03-24//",
|
||||
"-//w3c//dtd html 3.2 draft//",
|
||||
"-//w3c//dtd html 3.2 final//",
|
||||
"-//w3c//dtd html 3.2//",
|
||||
"-//w3c//dtd html 3.2s draft//",
|
||||
"-//w3c//dtd html 4.0 frameset//",
|
||||
"-//w3c//dtd html 4.0 transitional//",
|
||||
"-//w3c//dtd html experimental 19960712//",
|
||||
"-//w3c//dtd html experimental 970421//",
|
||||
"-//w3c//dtd w3 html//",
|
||||
"-//w3o//dtd w3 html 3.0//",
|
||||
"-//webtechs//dtd mozilla html 2.0//",
|
||||
"-//webtechs//dtd mozilla html//",
|
||||
}
|
||||
2253
vendor/golang.org/x/net/html/entity.go
generated
vendored
Normal file
2253
vendor/golang.org/x/net/html/entity.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
29
vendor/golang.org/x/net/html/entity_test.go
generated
vendored
Normal file
29
vendor/golang.org/x/net/html/entity_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright 2010 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 html
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func TestEntityLength(t *testing.T) {
|
||||
// We verify that the length of UTF-8 encoding of each value is <= 1 + len(key).
|
||||
// The +1 comes from the leading "&". This property implies that the length of
|
||||
// unescaped text is <= the length of escaped text.
|
||||
for k, v := range entity {
|
||||
if 1+len(k) < utf8.RuneLen(v) {
|
||||
t.Error("escaped entity &" + k + " is shorter than its UTF-8 encoding " + string(v))
|
||||
}
|
||||
if len(k) > longestEntityWithoutSemicolon && k[len(k)-1] != ';' {
|
||||
t.Errorf("entity name %s is %d characters, but longestEntityWithoutSemicolon=%d", k, len(k), longestEntityWithoutSemicolon)
|
||||
}
|
||||
}
|
||||
for k, v := range entity2 {
|
||||
if 1+len(k) < utf8.RuneLen(v[0])+utf8.RuneLen(v[1]) {
|
||||
t.Error("escaped entity &" + k + " is shorter than its UTF-8 encoding " + string(v[0]) + string(v[1]))
|
||||
}
|
||||
}
|
||||
}
|
||||
258
vendor/golang.org/x/net/html/escape.go
generated
vendored
Normal file
258
vendor/golang.org/x/net/html/escape.go
generated
vendored
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
// Copyright 2010 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 html
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// These replacements permit compatibility with old numeric entities that
|
||||
// assumed Windows-1252 encoding.
|
||||
// https://html.spec.whatwg.org/multipage/syntax.html#consume-a-character-reference
|
||||
var replacementTable = [...]rune{
|
||||
'\u20AC', // First entry is what 0x80 should be replaced with.
|
||||
'\u0081',
|
||||
'\u201A',
|
||||
'\u0192',
|
||||
'\u201E',
|
||||
'\u2026',
|
||||
'\u2020',
|
||||
'\u2021',
|
||||
'\u02C6',
|
||||
'\u2030',
|
||||
'\u0160',
|
||||
'\u2039',
|
||||
'\u0152',
|
||||
'\u008D',
|
||||
'\u017D',
|
||||
'\u008F',
|
||||
'\u0090',
|
||||
'\u2018',
|
||||
'\u2019',
|
||||
'\u201C',
|
||||
'\u201D',
|
||||
'\u2022',
|
||||
'\u2013',
|
||||
'\u2014',
|
||||
'\u02DC',
|
||||
'\u2122',
|
||||
'\u0161',
|
||||
'\u203A',
|
||||
'\u0153',
|
||||
'\u009D',
|
||||
'\u017E',
|
||||
'\u0178', // Last entry is 0x9F.
|
||||
// 0x00->'\uFFFD' is handled programmatically.
|
||||
// 0x0D->'\u000D' is a no-op.
|
||||
}
|
||||
|
||||
// unescapeEntity reads an entity like "<" from b[src:] and writes the
|
||||
// corresponding "<" to b[dst:], returning the incremented dst and src cursors.
|
||||
// Precondition: b[src] == '&' && dst <= src.
|
||||
// attribute should be true if parsing an attribute value.
|
||||
func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) {
|
||||
// https://html.spec.whatwg.org/multipage/syntax.html#consume-a-character-reference
|
||||
|
||||
// i starts at 1 because we already know that s[0] == '&'.
|
||||
i, s := 1, b[src:]
|
||||
|
||||
if len(s) <= 1 {
|
||||
b[dst] = b[src]
|
||||
return dst + 1, src + 1
|
||||
}
|
||||
|
||||
if s[i] == '#' {
|
||||
if len(s) <= 3 { // We need to have at least "&#.".
|
||||
b[dst] = b[src]
|
||||
return dst + 1, src + 1
|
||||
}
|
||||
i++
|
||||
c := s[i]
|
||||
hex := false
|
||||
if c == 'x' || c == 'X' {
|
||||
hex = true
|
||||
i++
|
||||
}
|
||||
|
||||
x := '\x00'
|
||||
for i < len(s) {
|
||||
c = s[i]
|
||||
i++
|
||||
if hex {
|
||||
if '0' <= c && c <= '9' {
|
||||
x = 16*x + rune(c) - '0'
|
||||
continue
|
||||
} else if 'a' <= c && c <= 'f' {
|
||||
x = 16*x + rune(c) - 'a' + 10
|
||||
continue
|
||||
} else if 'A' <= c && c <= 'F' {
|
||||
x = 16*x + rune(c) - 'A' + 10
|
||||
continue
|
||||
}
|
||||
} else if '0' <= c && c <= '9' {
|
||||
x = 10*x + rune(c) - '0'
|
||||
continue
|
||||
}
|
||||
if c != ';' {
|
||||
i--
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if i <= 3 { // No characters matched.
|
||||
b[dst] = b[src]
|
||||
return dst + 1, src + 1
|
||||
}
|
||||
|
||||
if 0x80 <= x && x <= 0x9F {
|
||||
// Replace characters from Windows-1252 with UTF-8 equivalents.
|
||||
x = replacementTable[x-0x80]
|
||||
} else if x == 0 || (0xD800 <= x && x <= 0xDFFF) || x > 0x10FFFF {
|
||||
// Replace invalid characters with the replacement character.
|
||||
x = '\uFFFD'
|
||||
}
|
||||
|
||||
return dst + utf8.EncodeRune(b[dst:], x), src + i
|
||||
}
|
||||
|
||||
// Consume the maximum number of characters possible, with the
|
||||
// consumed characters matching one of the named references.
|
||||
|
||||
for i < len(s) {
|
||||
c := s[i]
|
||||
i++
|
||||
// Lower-cased characters are more common in entities, so we check for them first.
|
||||
if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' {
|
||||
continue
|
||||
}
|
||||
if c != ';' {
|
||||
i--
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
entityName := string(s[1:i])
|
||||
if entityName == "" {
|
||||
// No-op.
|
||||
} else if attribute && entityName[len(entityName)-1] != ';' && len(s) > i && s[i] == '=' {
|
||||
// No-op.
|
||||
} else if x := entity[entityName]; x != 0 {
|
||||
return dst + utf8.EncodeRune(b[dst:], x), src + i
|
||||
} else if x := entity2[entityName]; x[0] != 0 {
|
||||
dst1 := dst + utf8.EncodeRune(b[dst:], x[0])
|
||||
return dst1 + utf8.EncodeRune(b[dst1:], x[1]), src + i
|
||||
} else if !attribute {
|
||||
maxLen := len(entityName) - 1
|
||||
if maxLen > longestEntityWithoutSemicolon {
|
||||
maxLen = longestEntityWithoutSemicolon
|
||||
}
|
||||
for j := maxLen; j > 1; j-- {
|
||||
if x := entity[entityName[:j]]; x != 0 {
|
||||
return dst + utf8.EncodeRune(b[dst:], x), src + j + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dst1, src1 = dst+i, src+i
|
||||
copy(b[dst:dst1], b[src:src1])
|
||||
return dst1, src1
|
||||
}
|
||||
|
||||
// unescape unescapes b's entities in-place, so that "a<b" becomes "a<b".
|
||||
// attribute should be true if parsing an attribute value.
|
||||
func unescape(b []byte, attribute bool) []byte {
|
||||
for i, c := range b {
|
||||
if c == '&' {
|
||||
dst, src := unescapeEntity(b, i, i, attribute)
|
||||
for src < len(b) {
|
||||
c := b[src]
|
||||
if c == '&' {
|
||||
dst, src = unescapeEntity(b, dst, src, attribute)
|
||||
} else {
|
||||
b[dst] = c
|
||||
dst, src = dst+1, src+1
|
||||
}
|
||||
}
|
||||
return b[0:dst]
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// lower lower-cases the A-Z bytes in b in-place, so that "aBc" becomes "abc".
|
||||
func lower(b []byte) []byte {
|
||||
for i, c := range b {
|
||||
if 'A' <= c && c <= 'Z' {
|
||||
b[i] = c + 'a' - 'A'
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
const escapedChars = "&'<>\"\r"
|
||||
|
||||
func escape(w writer, s string) error {
|
||||
i := strings.IndexAny(s, escapedChars)
|
||||
for i != -1 {
|
||||
if _, err := w.WriteString(s[:i]); err != nil {
|
||||
return err
|
||||
}
|
||||
var esc string
|
||||
switch s[i] {
|
||||
case '&':
|
||||
esc = "&"
|
||||
case '\'':
|
||||
// "'" is shorter than "'" and apos was not in HTML until HTML5.
|
||||
esc = "'"
|
||||
case '<':
|
||||
esc = "<"
|
||||
case '>':
|
||||
esc = ">"
|
||||
case '"':
|
||||
// """ is shorter than """.
|
||||
esc = """
|
||||
case '\r':
|
||||
esc = " "
|
||||
default:
|
||||
panic("unrecognized escape character")
|
||||
}
|
||||
s = s[i+1:]
|
||||
if _, err := w.WriteString(esc); err != nil {
|
||||
return err
|
||||
}
|
||||
i = strings.IndexAny(s, escapedChars)
|
||||
}
|
||||
_, err := w.WriteString(s)
|
||||
return err
|
||||
}
|
||||
|
||||
// EscapeString escapes special characters like "<" to become "<". It
|
||||
// escapes only five such characters: <, >, &, ' and ".
|
||||
// UnescapeString(EscapeString(s)) == s always holds, but the converse isn't
|
||||
// always true.
|
||||
func EscapeString(s string) string {
|
||||
if strings.IndexAny(s, escapedChars) == -1 {
|
||||
return s
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
escape(&buf, s)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// UnescapeString unescapes entities like "<" to become "<". It unescapes a
|
||||
// larger range of entities than EscapeString escapes. For example, "á"
|
||||
// unescapes to "á", as does "á" and "&xE1;".
|
||||
// UnescapeString(EscapeString(s)) == s always holds, but the converse isn't
|
||||
// always true.
|
||||
func UnescapeString(s string) string {
|
||||
for _, c := range s {
|
||||
if c == '&' {
|
||||
return string(unescape([]byte(s), false))
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
97
vendor/golang.org/x/net/html/escape_test.go
generated
vendored
Normal file
97
vendor/golang.org/x/net/html/escape_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package html
|
||||
|
||||
import "testing"
|
||||
|
||||
type unescapeTest struct {
|
||||
// A short description of the test case.
|
||||
desc string
|
||||
// The HTML text.
|
||||
html string
|
||||
// The unescaped text.
|
||||
unescaped string
|
||||
}
|
||||
|
||||
var unescapeTests = []unescapeTest{
|
||||
// Handle no entities.
|
||||
{
|
||||
"copy",
|
||||
"A\ttext\nstring",
|
||||
"A\ttext\nstring",
|
||||
},
|
||||
// Handle simple named entities.
|
||||
{
|
||||
"simple",
|
||||
"& > <",
|
||||
"& > <",
|
||||
},
|
||||
// Handle hitting the end of the string.
|
||||
{
|
||||
"stringEnd",
|
||||
"& &",
|
||||
"& &",
|
||||
},
|
||||
// Handle entities with two codepoints.
|
||||
{
|
||||
"multiCodepoint",
|
||||
"text ⋛︀ blah",
|
||||
"text \u22db\ufe00 blah",
|
||||
},
|
||||
// Handle decimal numeric entities.
|
||||
{
|
||||
"decimalEntity",
|
||||
"Delta = Δ ",
|
||||
"Delta = Δ ",
|
||||
},
|
||||
// Handle hexadecimal numeric entities.
|
||||
{
|
||||
"hexadecimalEntity",
|
||||
"Lambda = λ = λ ",
|
||||
"Lambda = λ = λ ",
|
||||
},
|
||||
// Handle numeric early termination.
|
||||
{
|
||||
"numericEnds",
|
||||
"&# &#x €43 © = ©f = ©",
|
||||
"&# &#x €43 © = ©f = ©",
|
||||
},
|
||||
// Handle numeric ISO-8859-1 entity replacements.
|
||||
{
|
||||
"numericReplacements",
|
||||
"Footnote‡",
|
||||
"Footnote‡",
|
||||
},
|
||||
}
|
||||
|
||||
func TestUnescape(t *testing.T) {
|
||||
for _, tt := range unescapeTests {
|
||||
unescaped := UnescapeString(tt.html)
|
||||
if unescaped != tt.unescaped {
|
||||
t.Errorf("TestUnescape %s: want %q, got %q", tt.desc, tt.unescaped, unescaped)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnescapeEscape(t *testing.T) {
|
||||
ss := []string{
|
||||
``,
|
||||
`abc def`,
|
||||
`a & b`,
|
||||
`a&b`,
|
||||
`a & b`,
|
||||
`"`,
|
||||
`"`,
|
||||
`"<&>"`,
|
||||
`"<&>"`,
|
||||
`3&5==1 && 0<1, "0<1", a+acute=á`,
|
||||
`The special characters are: <, >, &, ' and "`,
|
||||
}
|
||||
for _, s := range ss {
|
||||
if got := UnescapeString(EscapeString(s)); got != s {
|
||||
t.Errorf("got %q want %q", got, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
40
vendor/golang.org/x/net/html/example_test.go
generated
vendored
Normal file
40
vendor/golang.org/x/net/html/example_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
// Copyright 2012 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.
|
||||
|
||||
// This example demonstrates parsing HTML data and walking the resulting tree.
|
||||
package html_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
func ExampleParse() {
|
||||
s := `<p>Links:</p><ul><li><a href="foo">Foo</a><li><a href="/bar/baz">BarBaz</a></ul>`
|
||||
doc, err := html.Parse(strings.NewReader(s))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
var f func(*html.Node)
|
||||
f = func(n *html.Node) {
|
||||
if n.Type == html.ElementNode && n.Data == "a" {
|
||||
for _, a := range n.Attr {
|
||||
if a.Key == "href" {
|
||||
fmt.Println(a.Val)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
f(c)
|
||||
}
|
||||
}
|
||||
f(doc)
|
||||
// Output:
|
||||
// foo
|
||||
// /bar/baz
|
||||
}
|
||||
226
vendor/golang.org/x/net/html/foreign.go
generated
vendored
Normal file
226
vendor/golang.org/x/net/html/foreign.go
generated
vendored
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package html
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func adjustAttributeNames(aa []Attribute, nameMap map[string]string) {
|
||||
for i := range aa {
|
||||
if newName, ok := nameMap[aa[i].Key]; ok {
|
||||
aa[i].Key = newName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func adjustForeignAttributes(aa []Attribute) {
|
||||
for i, a := range aa {
|
||||
if a.Key == "" || a.Key[0] != 'x' {
|
||||
continue
|
||||
}
|
||||
switch a.Key {
|
||||
case "xlink:actuate", "xlink:arcrole", "xlink:href", "xlink:role", "xlink:show",
|
||||
"xlink:title", "xlink:type", "xml:base", "xml:lang", "xml:space", "xmlns:xlink":
|
||||
j := strings.Index(a.Key, ":")
|
||||
aa[i].Namespace = a.Key[:j]
|
||||
aa[i].Key = a.Key[j+1:]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func htmlIntegrationPoint(n *Node) bool {
|
||||
if n.Type != ElementNode {
|
||||
return false
|
||||
}
|
||||
switch n.Namespace {
|
||||
case "math":
|
||||
if n.Data == "annotation-xml" {
|
||||
for _, a := range n.Attr {
|
||||
if a.Key == "encoding" {
|
||||
val := strings.ToLower(a.Val)
|
||||
if val == "text/html" || val == "application/xhtml+xml" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case "svg":
|
||||
switch n.Data {
|
||||
case "desc", "foreignObject", "title":
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func mathMLTextIntegrationPoint(n *Node) bool {
|
||||
if n.Namespace != "math" {
|
||||
return false
|
||||
}
|
||||
switch n.Data {
|
||||
case "mi", "mo", "mn", "ms", "mtext":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Section 12.2.5.5.
|
||||
var breakout = map[string]bool{
|
||||
"b": true,
|
||||
"big": true,
|
||||
"blockquote": true,
|
||||
"body": true,
|
||||
"br": true,
|
||||
"center": true,
|
||||
"code": true,
|
||||
"dd": true,
|
||||
"div": true,
|
||||
"dl": true,
|
||||
"dt": true,
|
||||
"em": true,
|
||||
"embed": true,
|
||||
"h1": true,
|
||||
"h2": true,
|
||||
"h3": true,
|
||||
"h4": true,
|
||||
"h5": true,
|
||||
"h6": true,
|
||||
"head": true,
|
||||
"hr": true,
|
||||
"i": true,
|
||||
"img": true,
|
||||
"li": true,
|
||||
"listing": true,
|
||||
"menu": true,
|
||||
"meta": true,
|
||||
"nobr": true,
|
||||
"ol": true,
|
||||
"p": true,
|
||||
"pre": true,
|
||||
"ruby": true,
|
||||
"s": true,
|
||||
"small": true,
|
||||
"span": true,
|
||||
"strong": true,
|
||||
"strike": true,
|
||||
"sub": true,
|
||||
"sup": true,
|
||||
"table": true,
|
||||
"tt": true,
|
||||
"u": true,
|
||||
"ul": true,
|
||||
"var": true,
|
||||
}
|
||||
|
||||
// Section 12.2.5.5.
|
||||
var svgTagNameAdjustments = map[string]string{
|
||||
"altglyph": "altGlyph",
|
||||
"altglyphdef": "altGlyphDef",
|
||||
"altglyphitem": "altGlyphItem",
|
||||
"animatecolor": "animateColor",
|
||||
"animatemotion": "animateMotion",
|
||||
"animatetransform": "animateTransform",
|
||||
"clippath": "clipPath",
|
||||
"feblend": "feBlend",
|
||||
"fecolormatrix": "feColorMatrix",
|
||||
"fecomponenttransfer": "feComponentTransfer",
|
||||
"fecomposite": "feComposite",
|
||||
"feconvolvematrix": "feConvolveMatrix",
|
||||
"fediffuselighting": "feDiffuseLighting",
|
||||
"fedisplacementmap": "feDisplacementMap",
|
||||
"fedistantlight": "feDistantLight",
|
||||
"feflood": "feFlood",
|
||||
"fefunca": "feFuncA",
|
||||
"fefuncb": "feFuncB",
|
||||
"fefuncg": "feFuncG",
|
||||
"fefuncr": "feFuncR",
|
||||
"fegaussianblur": "feGaussianBlur",
|
||||
"feimage": "feImage",
|
||||
"femerge": "feMerge",
|
||||
"femergenode": "feMergeNode",
|
||||
"femorphology": "feMorphology",
|
||||
"feoffset": "feOffset",
|
||||
"fepointlight": "fePointLight",
|
||||
"fespecularlighting": "feSpecularLighting",
|
||||
"fespotlight": "feSpotLight",
|
||||
"fetile": "feTile",
|
||||
"feturbulence": "feTurbulence",
|
||||
"foreignobject": "foreignObject",
|
||||
"glyphref": "glyphRef",
|
||||
"lineargradient": "linearGradient",
|
||||
"radialgradient": "radialGradient",
|
||||
"textpath": "textPath",
|
||||
}
|
||||
|
||||
// Section 12.2.5.1
|
||||
var mathMLAttributeAdjustments = map[string]string{
|
||||
"definitionurl": "definitionURL",
|
||||
}
|
||||
|
||||
var svgAttributeAdjustments = map[string]string{
|
||||
"attributename": "attributeName",
|
||||
"attributetype": "attributeType",
|
||||
"basefrequency": "baseFrequency",
|
||||
"baseprofile": "baseProfile",
|
||||
"calcmode": "calcMode",
|
||||
"clippathunits": "clipPathUnits",
|
||||
"contentscripttype": "contentScriptType",
|
||||
"contentstyletype": "contentStyleType",
|
||||
"diffuseconstant": "diffuseConstant",
|
||||
"edgemode": "edgeMode",
|
||||
"externalresourcesrequired": "externalResourcesRequired",
|
||||
"filterres": "filterRes",
|
||||
"filterunits": "filterUnits",
|
||||
"glyphref": "glyphRef",
|
||||
"gradienttransform": "gradientTransform",
|
||||
"gradientunits": "gradientUnits",
|
||||
"kernelmatrix": "kernelMatrix",
|
||||
"kernelunitlength": "kernelUnitLength",
|
||||
"keypoints": "keyPoints",
|
||||
"keysplines": "keySplines",
|
||||
"keytimes": "keyTimes",
|
||||
"lengthadjust": "lengthAdjust",
|
||||
"limitingconeangle": "limitingConeAngle",
|
||||
"markerheight": "markerHeight",
|
||||
"markerunits": "markerUnits",
|
||||
"markerwidth": "markerWidth",
|
||||
"maskcontentunits": "maskContentUnits",
|
||||
"maskunits": "maskUnits",
|
||||
"numoctaves": "numOctaves",
|
||||
"pathlength": "pathLength",
|
||||
"patterncontentunits": "patternContentUnits",
|
||||
"patterntransform": "patternTransform",
|
||||
"patternunits": "patternUnits",
|
||||
"pointsatx": "pointsAtX",
|
||||
"pointsaty": "pointsAtY",
|
||||
"pointsatz": "pointsAtZ",
|
||||
"preservealpha": "preserveAlpha",
|
||||
"preserveaspectratio": "preserveAspectRatio",
|
||||
"primitiveunits": "primitiveUnits",
|
||||
"refx": "refX",
|
||||
"refy": "refY",
|
||||
"repeatcount": "repeatCount",
|
||||
"repeatdur": "repeatDur",
|
||||
"requiredextensions": "requiredExtensions",
|
||||
"requiredfeatures": "requiredFeatures",
|
||||
"specularconstant": "specularConstant",
|
||||
"specularexponent": "specularExponent",
|
||||
"spreadmethod": "spreadMethod",
|
||||
"startoffset": "startOffset",
|
||||
"stddeviation": "stdDeviation",
|
||||
"stitchtiles": "stitchTiles",
|
||||
"surfacescale": "surfaceScale",
|
||||
"systemlanguage": "systemLanguage",
|
||||
"tablevalues": "tableValues",
|
||||
"targetx": "targetX",
|
||||
"targety": "targetY",
|
||||
"textlength": "textLength",
|
||||
"viewbox": "viewBox",
|
||||
"viewtarget": "viewTarget",
|
||||
"xchannelselector": "xChannelSelector",
|
||||
"ychannelselector": "yChannelSelector",
|
||||
"zoomandpan": "zoomAndPan",
|
||||
}
|
||||
193
vendor/golang.org/x/net/html/node.go
generated
vendored
Normal file
193
vendor/golang.org/x/net/html/node.go
generated
vendored
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package html
|
||||
|
||||
import (
|
||||
"golang.org/x/net/html/atom"
|
||||
)
|
||||
|
||||
// A NodeType is the type of a Node.
|
||||
type NodeType uint32
|
||||
|
||||
const (
|
||||
ErrorNode NodeType = iota
|
||||
TextNode
|
||||
DocumentNode
|
||||
ElementNode
|
||||
CommentNode
|
||||
DoctypeNode
|
||||
scopeMarkerNode
|
||||
)
|
||||
|
||||
// Section 12.2.3.3 says "scope markers are inserted when entering applet
|
||||
// elements, buttons, object elements, marquees, table cells, and table
|
||||
// captions, and are used to prevent formatting from 'leaking'".
|
||||
var scopeMarker = Node{Type: scopeMarkerNode}
|
||||
|
||||
// A Node consists of a NodeType and some Data (tag name for element nodes,
|
||||
// content for text) and are part of a tree of Nodes. Element nodes may also
|
||||
// have a Namespace and contain a slice of Attributes. Data is unescaped, so
|
||||
// that it looks like "a<b" rather than "a<b". For element nodes, DataAtom
|
||||
// is the atom for Data, or zero if Data is not a known tag name.
|
||||
//
|
||||
// An empty Namespace implies a "http://www.w3.org/1999/xhtml" namespace.
|
||||
// Similarly, "math" is short for "http://www.w3.org/1998/Math/MathML", and
|
||||
// "svg" is short for "http://www.w3.org/2000/svg".
|
||||
type Node struct {
|
||||
Parent, FirstChild, LastChild, PrevSibling, NextSibling *Node
|
||||
|
||||
Type NodeType
|
||||
DataAtom atom.Atom
|
||||
Data string
|
||||
Namespace string
|
||||
Attr []Attribute
|
||||
}
|
||||
|
||||
// InsertBefore inserts newChild as a child of n, immediately before oldChild
|
||||
// in the sequence of n's children. oldChild may be nil, in which case newChild
|
||||
// is appended to the end of n's children.
|
||||
//
|
||||
// It will panic if newChild already has a parent or siblings.
|
||||
func (n *Node) InsertBefore(newChild, oldChild *Node) {
|
||||
if newChild.Parent != nil || newChild.PrevSibling != nil || newChild.NextSibling != nil {
|
||||
panic("html: InsertBefore called for an attached child Node")
|
||||
}
|
||||
var prev, next *Node
|
||||
if oldChild != nil {
|
||||
prev, next = oldChild.PrevSibling, oldChild
|
||||
} else {
|
||||
prev = n.LastChild
|
||||
}
|
||||
if prev != nil {
|
||||
prev.NextSibling = newChild
|
||||
} else {
|
||||
n.FirstChild = newChild
|
||||
}
|
||||
if next != nil {
|
||||
next.PrevSibling = newChild
|
||||
} else {
|
||||
n.LastChild = newChild
|
||||
}
|
||||
newChild.Parent = n
|
||||
newChild.PrevSibling = prev
|
||||
newChild.NextSibling = next
|
||||
}
|
||||
|
||||
// AppendChild adds a node c as a child of n.
|
||||
//
|
||||
// It will panic if c already has a parent or siblings.
|
||||
func (n *Node) AppendChild(c *Node) {
|
||||
if c.Parent != nil || c.PrevSibling != nil || c.NextSibling != nil {
|
||||
panic("html: AppendChild called for an attached child Node")
|
||||
}
|
||||
last := n.LastChild
|
||||
if last != nil {
|
||||
last.NextSibling = c
|
||||
} else {
|
||||
n.FirstChild = c
|
||||
}
|
||||
n.LastChild = c
|
||||
c.Parent = n
|
||||
c.PrevSibling = last
|
||||
}
|
||||
|
||||
// RemoveChild removes a node c that is a child of n. Afterwards, c will have
|
||||
// no parent and no siblings.
|
||||
//
|
||||
// It will panic if c's parent is not n.
|
||||
func (n *Node) RemoveChild(c *Node) {
|
||||
if c.Parent != n {
|
||||
panic("html: RemoveChild called for a non-child Node")
|
||||
}
|
||||
if n.FirstChild == c {
|
||||
n.FirstChild = c.NextSibling
|
||||
}
|
||||
if c.NextSibling != nil {
|
||||
c.NextSibling.PrevSibling = c.PrevSibling
|
||||
}
|
||||
if n.LastChild == c {
|
||||
n.LastChild = c.PrevSibling
|
||||
}
|
||||
if c.PrevSibling != nil {
|
||||
c.PrevSibling.NextSibling = c.NextSibling
|
||||
}
|
||||
c.Parent = nil
|
||||
c.PrevSibling = nil
|
||||
c.NextSibling = nil
|
||||
}
|
||||
|
||||
// reparentChildren reparents all of src's child nodes to dst.
|
||||
func reparentChildren(dst, src *Node) {
|
||||
for {
|
||||
child := src.FirstChild
|
||||
if child == nil {
|
||||
break
|
||||
}
|
||||
src.RemoveChild(child)
|
||||
dst.AppendChild(child)
|
||||
}
|
||||
}
|
||||
|
||||
// clone returns a new node with the same type, data and attributes.
|
||||
// The clone has no parent, no siblings and no children.
|
||||
func (n *Node) clone() *Node {
|
||||
m := &Node{
|
||||
Type: n.Type,
|
||||
DataAtom: n.DataAtom,
|
||||
Data: n.Data,
|
||||
Attr: make([]Attribute, len(n.Attr)),
|
||||
}
|
||||
copy(m.Attr, n.Attr)
|
||||
return m
|
||||
}
|
||||
|
||||
// nodeStack is a stack of nodes.
|
||||
type nodeStack []*Node
|
||||
|
||||
// pop pops the stack. It will panic if s is empty.
|
||||
func (s *nodeStack) pop() *Node {
|
||||
i := len(*s)
|
||||
n := (*s)[i-1]
|
||||
*s = (*s)[:i-1]
|
||||
return n
|
||||
}
|
||||
|
||||
// top returns the most recently pushed node, or nil if s is empty.
|
||||
func (s *nodeStack) top() *Node {
|
||||
if i := len(*s); i > 0 {
|
||||
return (*s)[i-1]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// index returns the index of the top-most occurrence of n in the stack, or -1
|
||||
// if n is not present.
|
||||
func (s *nodeStack) index(n *Node) int {
|
||||
for i := len(*s) - 1; i >= 0; i-- {
|
||||
if (*s)[i] == n {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// insert inserts a node at the given index.
|
||||
func (s *nodeStack) insert(i int, n *Node) {
|
||||
(*s) = append(*s, nil)
|
||||
copy((*s)[i+1:], (*s)[i:])
|
||||
(*s)[i] = n
|
||||
}
|
||||
|
||||
// remove removes a node from the stack. It is a no-op if n is not present.
|
||||
func (s *nodeStack) remove(n *Node) {
|
||||
i := s.index(n)
|
||||
if i == -1 {
|
||||
return
|
||||
}
|
||||
copy((*s)[i:], (*s)[i+1:])
|
||||
j := len(*s) - 1
|
||||
(*s)[j] = nil
|
||||
*s = (*s)[:j]
|
||||
}
|
||||
146
vendor/golang.org/x/net/html/node_test.go
generated
vendored
Normal file
146
vendor/golang.org/x/net/html/node_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
// Copyright 2010 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 html
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// checkTreeConsistency checks that a node and its descendants are all
|
||||
// consistent in their parent/child/sibling relationships.
|
||||
func checkTreeConsistency(n *Node) error {
|
||||
return checkTreeConsistency1(n, 0)
|
||||
}
|
||||
|
||||
func checkTreeConsistency1(n *Node, depth int) error {
|
||||
if depth == 1e4 {
|
||||
return fmt.Errorf("html: tree looks like it contains a cycle")
|
||||
}
|
||||
if err := checkNodeConsistency(n); err != nil {
|
||||
return err
|
||||
}
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
if err := checkTreeConsistency1(c, depth+1); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkNodeConsistency checks that a node's parent/child/sibling relationships
|
||||
// are consistent.
|
||||
func checkNodeConsistency(n *Node) error {
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
nParent := 0
|
||||
for p := n.Parent; p != nil; p = p.Parent {
|
||||
nParent++
|
||||
if nParent == 1e4 {
|
||||
return fmt.Errorf("html: parent list looks like an infinite loop")
|
||||
}
|
||||
}
|
||||
|
||||
nForward := 0
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
nForward++
|
||||
if nForward == 1e6 {
|
||||
return fmt.Errorf("html: forward list of children looks like an infinite loop")
|
||||
}
|
||||
if c.Parent != n {
|
||||
return fmt.Errorf("html: inconsistent child/parent relationship")
|
||||
}
|
||||
}
|
||||
|
||||
nBackward := 0
|
||||
for c := n.LastChild; c != nil; c = c.PrevSibling {
|
||||
nBackward++
|
||||
if nBackward == 1e6 {
|
||||
return fmt.Errorf("html: backward list of children looks like an infinite loop")
|
||||
}
|
||||
if c.Parent != n {
|
||||
return fmt.Errorf("html: inconsistent child/parent relationship")
|
||||
}
|
||||
}
|
||||
|
||||
if n.Parent != nil {
|
||||
if n.Parent == n {
|
||||
return fmt.Errorf("html: inconsistent parent relationship")
|
||||
}
|
||||
if n.Parent == n.FirstChild {
|
||||
return fmt.Errorf("html: inconsistent parent/first relationship")
|
||||
}
|
||||
if n.Parent == n.LastChild {
|
||||
return fmt.Errorf("html: inconsistent parent/last relationship")
|
||||
}
|
||||
if n.Parent == n.PrevSibling {
|
||||
return fmt.Errorf("html: inconsistent parent/prev relationship")
|
||||
}
|
||||
if n.Parent == n.NextSibling {
|
||||
return fmt.Errorf("html: inconsistent parent/next relationship")
|
||||
}
|
||||
|
||||
parentHasNAsAChild := false
|
||||
for c := n.Parent.FirstChild; c != nil; c = c.NextSibling {
|
||||
if c == n {
|
||||
parentHasNAsAChild = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !parentHasNAsAChild {
|
||||
return fmt.Errorf("html: inconsistent parent/child relationship")
|
||||
}
|
||||
}
|
||||
|
||||
if n.PrevSibling != nil && n.PrevSibling.NextSibling != n {
|
||||
return fmt.Errorf("html: inconsistent prev/next relationship")
|
||||
}
|
||||
if n.NextSibling != nil && n.NextSibling.PrevSibling != n {
|
||||
return fmt.Errorf("html: inconsistent next/prev relationship")
|
||||
}
|
||||
|
||||
if (n.FirstChild == nil) != (n.LastChild == nil) {
|
||||
return fmt.Errorf("html: inconsistent first/last relationship")
|
||||
}
|
||||
if n.FirstChild != nil && n.FirstChild == n.LastChild {
|
||||
// We have a sole child.
|
||||
if n.FirstChild.PrevSibling != nil || n.FirstChild.NextSibling != nil {
|
||||
return fmt.Errorf("html: inconsistent sole child's sibling relationship")
|
||||
}
|
||||
}
|
||||
|
||||
seen := map[*Node]bool{}
|
||||
|
||||
var last *Node
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
if seen[c] {
|
||||
return fmt.Errorf("html: inconsistent repeated child")
|
||||
}
|
||||
seen[c] = true
|
||||
last = c
|
||||
}
|
||||
if last != n.LastChild {
|
||||
return fmt.Errorf("html: inconsistent last relationship")
|
||||
}
|
||||
|
||||
var first *Node
|
||||
for c := n.LastChild; c != nil; c = c.PrevSibling {
|
||||
if !seen[c] {
|
||||
return fmt.Errorf("html: inconsistent missing child")
|
||||
}
|
||||
delete(seen, c)
|
||||
first = c
|
||||
}
|
||||
if first != n.FirstChild {
|
||||
return fmt.Errorf("html: inconsistent first relationship")
|
||||
}
|
||||
|
||||
if len(seen) != 0 {
|
||||
return fmt.Errorf("html: inconsistent forwards/backwards child list")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
2094
vendor/golang.org/x/net/html/parse.go
generated
vendored
Normal file
2094
vendor/golang.org/x/net/html/parse.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
388
vendor/golang.org/x/net/html/parse_test.go
generated
vendored
Normal file
388
vendor/golang.org/x/net/html/parse_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,388 @@
|
|||
// Copyright 2010 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 html
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/html/atom"
|
||||
)
|
||||
|
||||
// readParseTest reads a single test case from r.
|
||||
func readParseTest(r *bufio.Reader) (text, want, context string, err error) {
|
||||
line, err := r.ReadSlice('\n')
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
var b []byte
|
||||
|
||||
// Read the HTML.
|
||||
if string(line) != "#data\n" {
|
||||
return "", "", "", fmt.Errorf(`got %q want "#data\n"`, line)
|
||||
}
|
||||
for {
|
||||
line, err = r.ReadSlice('\n')
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
if line[0] == '#' {
|
||||
break
|
||||
}
|
||||
b = append(b, line...)
|
||||
}
|
||||
text = strings.TrimSuffix(string(b), "\n")
|
||||
b = b[:0]
|
||||
|
||||
// Skip the error list.
|
||||
if string(line) != "#errors\n" {
|
||||
return "", "", "", fmt.Errorf(`got %q want "#errors\n"`, line)
|
||||
}
|
||||
for {
|
||||
line, err = r.ReadSlice('\n')
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
if line[0] == '#' {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if string(line) == "#document-fragment\n" {
|
||||
line, err = r.ReadSlice('\n')
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
context = strings.TrimSpace(string(line))
|
||||
line, err = r.ReadSlice('\n')
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
}
|
||||
|
||||
// Read the dump of what the parse tree should be.
|
||||
if string(line) != "#document\n" {
|
||||
return "", "", "", fmt.Errorf(`got %q want "#document\n"`, line)
|
||||
}
|
||||
inQuote := false
|
||||
for {
|
||||
line, err = r.ReadSlice('\n')
|
||||
if err != nil && err != io.EOF {
|
||||
return "", "", "", err
|
||||
}
|
||||
trimmed := bytes.Trim(line, "| \n")
|
||||
if len(trimmed) > 0 {
|
||||
if line[0] == '|' && trimmed[0] == '"' {
|
||||
inQuote = true
|
||||
}
|
||||
if trimmed[len(trimmed)-1] == '"' && !(line[0] == '|' && len(trimmed) == 1) {
|
||||
inQuote = false
|
||||
}
|
||||
}
|
||||
if len(line) == 0 || len(line) == 1 && line[0] == '\n' && !inQuote {
|
||||
break
|
||||
}
|
||||
b = append(b, line...)
|
||||
}
|
||||
return text, string(b), context, nil
|
||||
}
|
||||
|
||||
func dumpIndent(w io.Writer, level int) {
|
||||
io.WriteString(w, "| ")
|
||||
for i := 0; i < level; i++ {
|
||||
io.WriteString(w, " ")
|
||||
}
|
||||
}
|
||||
|
||||
type sortedAttributes []Attribute
|
||||
|
||||
func (a sortedAttributes) Len() int {
|
||||
return len(a)
|
||||
}
|
||||
|
||||
func (a sortedAttributes) Less(i, j int) bool {
|
||||
if a[i].Namespace != a[j].Namespace {
|
||||
return a[i].Namespace < a[j].Namespace
|
||||
}
|
||||
return a[i].Key < a[j].Key
|
||||
}
|
||||
|
||||
func (a sortedAttributes) Swap(i, j int) {
|
||||
a[i], a[j] = a[j], a[i]
|
||||
}
|
||||
|
||||
func dumpLevel(w io.Writer, n *Node, level int) error {
|
||||
dumpIndent(w, level)
|
||||
switch n.Type {
|
||||
case ErrorNode:
|
||||
return errors.New("unexpected ErrorNode")
|
||||
case DocumentNode:
|
||||
return errors.New("unexpected DocumentNode")
|
||||
case ElementNode:
|
||||
if n.Namespace != "" {
|
||||
fmt.Fprintf(w, "<%s %s>", n.Namespace, n.Data)
|
||||
} else {
|
||||
fmt.Fprintf(w, "<%s>", n.Data)
|
||||
}
|
||||
attr := sortedAttributes(n.Attr)
|
||||
sort.Sort(attr)
|
||||
for _, a := range attr {
|
||||
io.WriteString(w, "\n")
|
||||
dumpIndent(w, level+1)
|
||||
if a.Namespace != "" {
|
||||
fmt.Fprintf(w, `%s %s="%s"`, a.Namespace, a.Key, a.Val)
|
||||
} else {
|
||||
fmt.Fprintf(w, `%s="%s"`, a.Key, a.Val)
|
||||
}
|
||||
}
|
||||
case TextNode:
|
||||
fmt.Fprintf(w, `"%s"`, n.Data)
|
||||
case CommentNode:
|
||||
fmt.Fprintf(w, "<!-- %s -->", n.Data)
|
||||
case DoctypeNode:
|
||||
fmt.Fprintf(w, "<!DOCTYPE %s", n.Data)
|
||||
if n.Attr != nil {
|
||||
var p, s string
|
||||
for _, a := range n.Attr {
|
||||
switch a.Key {
|
||||
case "public":
|
||||
p = a.Val
|
||||
case "system":
|
||||
s = a.Val
|
||||
}
|
||||
}
|
||||
if p != "" || s != "" {
|
||||
fmt.Fprintf(w, ` "%s"`, p)
|
||||
fmt.Fprintf(w, ` "%s"`, s)
|
||||
}
|
||||
}
|
||||
io.WriteString(w, ">")
|
||||
case scopeMarkerNode:
|
||||
return errors.New("unexpected scopeMarkerNode")
|
||||
default:
|
||||
return errors.New("unknown node type")
|
||||
}
|
||||
io.WriteString(w, "\n")
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
if err := dumpLevel(w, c, level+1); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func dump(n *Node) (string, error) {
|
||||
if n == nil || n.FirstChild == nil {
|
||||
return "", nil
|
||||
}
|
||||
var b bytes.Buffer
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
if err := dumpLevel(&b, c, 0); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return b.String(), nil
|
||||
}
|
||||
|
||||
const testDataDir = "testdata/webkit/"
|
||||
|
||||
func TestParser(t *testing.T) {
|
||||
testFiles, err := filepath.Glob(testDataDir + "*.dat")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, tf := range testFiles {
|
||||
f, err := os.Open(tf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
r := bufio.NewReader(f)
|
||||
|
||||
for i := 0; ; i++ {
|
||||
text, want, context, err := readParseTest(r)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = testParseCase(text, want, context)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("%s test #%d %q, %s", tf, i, text, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// testParseCase tests one test case from the test files. If the test does not
|
||||
// pass, it returns an error that explains the failure.
|
||||
// text is the HTML to be parsed, want is a dump of the correct parse tree,
|
||||
// and context is the name of the context node, if any.
|
||||
func testParseCase(text, want, context string) (err error) {
|
||||
defer func() {
|
||||
if x := recover(); x != nil {
|
||||
switch e := x.(type) {
|
||||
case error:
|
||||
err = e
|
||||
default:
|
||||
err = fmt.Errorf("%v", e)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
var doc *Node
|
||||
if context == "" {
|
||||
doc, err = Parse(strings.NewReader(text))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
contextNode := &Node{
|
||||
Type: ElementNode,
|
||||
DataAtom: atom.Lookup([]byte(context)),
|
||||
Data: context,
|
||||
}
|
||||
nodes, err := ParseFragment(strings.NewReader(text), contextNode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
doc = &Node{
|
||||
Type: DocumentNode,
|
||||
}
|
||||
for _, n := range nodes {
|
||||
doc.AppendChild(n)
|
||||
}
|
||||
}
|
||||
|
||||
if err := checkTreeConsistency(doc); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
got, err := dump(doc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Compare the parsed tree to the #document section.
|
||||
if got != want {
|
||||
return fmt.Errorf("got vs want:\n----\n%s----\n%s----", got, want)
|
||||
}
|
||||
|
||||
if renderTestBlacklist[text] || context != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check that rendering and re-parsing results in an identical tree.
|
||||
pr, pw := io.Pipe()
|
||||
go func() {
|
||||
pw.CloseWithError(Render(pw, doc))
|
||||
}()
|
||||
doc1, err := Parse(pr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
got1, err := dump(doc1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if got != got1 {
|
||||
return fmt.Errorf("got vs got1:\n----\n%s----\n%s----", got, got1)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Some test input result in parse trees are not 'well-formed' despite
|
||||
// following the HTML5 recovery algorithms. Rendering and re-parsing such a
|
||||
// tree will not result in an exact clone of that tree. We blacklist such
|
||||
// inputs from the render test.
|
||||
var renderTestBlacklist = map[string]bool{
|
||||
// The second <a> will be reparented to the first <table>'s parent. This
|
||||
// results in an <a> whose parent is an <a>, which is not 'well-formed'.
|
||||
`<a><table><td><a><table></table><a></tr><a></table><b>X</b>C<a>Y`: true,
|
||||
// The same thing with a <p>:
|
||||
`<p><table></p>`: true,
|
||||
// More cases of <a> being reparented:
|
||||
`<a href="blah">aba<table><a href="foo">br<tr><td></td></tr>x</table>aoe`: true,
|
||||
`<a><table><a></table><p><a><div><a>`: true,
|
||||
`<a><table><td><a><table></table><a></tr><a></table><a>`: true,
|
||||
// A similar reparenting situation involving <nobr>:
|
||||
`<!DOCTYPE html><body><b><nobr>1<table><nobr></b><i><nobr>2<nobr></i>3`: true,
|
||||
// A <plaintext> element is reparented, putting it before a table.
|
||||
// A <plaintext> element can't have anything after it in HTML.
|
||||
`<table><plaintext><td>`: true,
|
||||
`<!doctype html><table><plaintext></plaintext>`: true,
|
||||
`<!doctype html><table><tbody><plaintext></plaintext>`: true,
|
||||
`<!doctype html><table><tbody><tr><plaintext></plaintext>`: true,
|
||||
// A form inside a table inside a form doesn't work either.
|
||||
`<!doctype html><form><table></form><form></table></form>`: true,
|
||||
// A script that ends at EOF may escape its own closing tag when rendered.
|
||||
`<!doctype html><script><!--<script `: true,
|
||||
`<!doctype html><script><!--<script <`: true,
|
||||
`<!doctype html><script><!--<script <a`: true,
|
||||
`<!doctype html><script><!--<script </`: true,
|
||||
`<!doctype html><script><!--<script </s`: true,
|
||||
`<!doctype html><script><!--<script </script`: true,
|
||||
`<!doctype html><script><!--<script </scripta`: true,
|
||||
`<!doctype html><script><!--<script -`: true,
|
||||
`<!doctype html><script><!--<script -a`: true,
|
||||
`<!doctype html><script><!--<script -<`: true,
|
||||
`<!doctype html><script><!--<script --`: true,
|
||||
`<!doctype html><script><!--<script --a`: true,
|
||||
`<!doctype html><script><!--<script --<`: true,
|
||||
`<script><!--<script `: true,
|
||||
`<script><!--<script <a`: true,
|
||||
`<script><!--<script </script`: true,
|
||||
`<script><!--<script </scripta`: true,
|
||||
`<script><!--<script -`: true,
|
||||
`<script><!--<script -a`: true,
|
||||
`<script><!--<script --`: true,
|
||||
`<script><!--<script --a`: true,
|
||||
`<script><!--<script <`: true,
|
||||
`<script><!--<script </`: true,
|
||||
`<script><!--<script </s`: true,
|
||||
// Reconstructing the active formatting elements results in a <plaintext>
|
||||
// element that contains an <a> element.
|
||||
`<!doctype html><p><a><plaintext>b`: true,
|
||||
}
|
||||
|
||||
func TestNodeConsistency(t *testing.T) {
|
||||
// inconsistentNode is a Node whose DataAtom and Data do not agree.
|
||||
inconsistentNode := &Node{
|
||||
Type: ElementNode,
|
||||
DataAtom: atom.Frameset,
|
||||
Data: "table",
|
||||
}
|
||||
_, err := ParseFragment(strings.NewReader("<p>hello</p>"), inconsistentNode)
|
||||
if err == nil {
|
||||
t.Errorf("got nil error, want non-nil")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParser(b *testing.B) {
|
||||
buf, err := ioutil.ReadFile("testdata/go1.html")
|
||||
if err != nil {
|
||||
b.Fatalf("could not read testdata/go1.html: %v", err)
|
||||
}
|
||||
b.SetBytes(int64(len(buf)))
|
||||
runtime.GC()
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
Parse(bytes.NewBuffer(buf))
|
||||
}
|
||||
}
|
||||
271
vendor/golang.org/x/net/html/render.go
generated
vendored
Normal file
271
vendor/golang.org/x/net/html/render.go
generated
vendored
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package html
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type writer interface {
|
||||
io.Writer
|
||||
io.ByteWriter
|
||||
WriteString(string) (int, error)
|
||||
}
|
||||
|
||||
// Render renders the parse tree n to the given writer.
|
||||
//
|
||||
// Rendering is done on a 'best effort' basis: calling Parse on the output of
|
||||
// Render will always result in something similar to the original tree, but it
|
||||
// is not necessarily an exact clone unless the original tree was 'well-formed'.
|
||||
// 'Well-formed' is not easily specified; the HTML5 specification is
|
||||
// complicated.
|
||||
//
|
||||
// Calling Parse on arbitrary input typically results in a 'well-formed' parse
|
||||
// tree. However, it is possible for Parse to yield a 'badly-formed' parse tree.
|
||||
// For example, in a 'well-formed' parse tree, no <a> element is a child of
|
||||
// another <a> element: parsing "<a><a>" results in two sibling elements.
|
||||
// Similarly, in a 'well-formed' parse tree, no <a> element is a child of a
|
||||
// <table> element: parsing "<p><table><a>" results in a <p> with two sibling
|
||||
// children; the <a> is reparented to the <table>'s parent. However, calling
|
||||
// Parse on "<a><table><a>" does not return an error, but the result has an <a>
|
||||
// element with an <a> child, and is therefore not 'well-formed'.
|
||||
//
|
||||
// Programmatically constructed trees are typically also 'well-formed', but it
|
||||
// is possible to construct a tree that looks innocuous but, when rendered and
|
||||
// re-parsed, results in a different tree. A simple example is that a solitary
|
||||
// text node would become a tree containing <html>, <head> and <body> elements.
|
||||
// Another example is that the programmatic equivalent of "a<head>b</head>c"
|
||||
// becomes "<html><head><head/><body>abc</body></html>".
|
||||
func Render(w io.Writer, n *Node) error {
|
||||
if x, ok := w.(writer); ok {
|
||||
return render(x, n)
|
||||
}
|
||||
buf := bufio.NewWriter(w)
|
||||
if err := render(buf, n); err != nil {
|
||||
return err
|
||||
}
|
||||
return buf.Flush()
|
||||
}
|
||||
|
||||
// plaintextAbort is returned from render1 when a <plaintext> element
|
||||
// has been rendered. No more end tags should be rendered after that.
|
||||
var plaintextAbort = errors.New("html: internal error (plaintext abort)")
|
||||
|
||||
func render(w writer, n *Node) error {
|
||||
err := render1(w, n)
|
||||
if err == plaintextAbort {
|
||||
err = nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func render1(w writer, n *Node) error {
|
||||
// Render non-element nodes; these are the easy cases.
|
||||
switch n.Type {
|
||||
case ErrorNode:
|
||||
return errors.New("html: cannot render an ErrorNode node")
|
||||
case TextNode:
|
||||
return escape(w, n.Data)
|
||||
case DocumentNode:
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
if err := render1(w, c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
case ElementNode:
|
||||
// No-op.
|
||||
case CommentNode:
|
||||
if _, err := w.WriteString("<!--"); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.WriteString(n.Data); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.WriteString("-->"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
case DoctypeNode:
|
||||
if _, err := w.WriteString("<!DOCTYPE "); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.WriteString(n.Data); err != nil {
|
||||
return err
|
||||
}
|
||||
if n.Attr != nil {
|
||||
var p, s string
|
||||
for _, a := range n.Attr {
|
||||
switch a.Key {
|
||||
case "public":
|
||||
p = a.Val
|
||||
case "system":
|
||||
s = a.Val
|
||||
}
|
||||
}
|
||||
if p != "" {
|
||||
if _, err := w.WriteString(" PUBLIC "); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writeQuoted(w, p); err != nil {
|
||||
return err
|
||||
}
|
||||
if s != "" {
|
||||
if err := w.WriteByte(' '); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writeQuoted(w, s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else if s != "" {
|
||||
if _, err := w.WriteString(" SYSTEM "); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writeQuoted(w, s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return w.WriteByte('>')
|
||||
default:
|
||||
return errors.New("html: unknown node type")
|
||||
}
|
||||
|
||||
// Render the <xxx> opening tag.
|
||||
if err := w.WriteByte('<'); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.WriteString(n.Data); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, a := range n.Attr {
|
||||
if err := w.WriteByte(' '); err != nil {
|
||||
return err
|
||||
}
|
||||
if a.Namespace != "" {
|
||||
if _, err := w.WriteString(a.Namespace); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := w.WriteByte(':'); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err := w.WriteString(a.Key); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.WriteString(`="`); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := escape(w, a.Val); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := w.WriteByte('"'); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if voidElements[n.Data] {
|
||||
if n.FirstChild != nil {
|
||||
return fmt.Errorf("html: void element <%s> has child nodes", n.Data)
|
||||
}
|
||||
_, err := w.WriteString("/>")
|
||||
return err
|
||||
}
|
||||
if err := w.WriteByte('>'); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add initial newline where there is danger of a newline beging ignored.
|
||||
if c := n.FirstChild; c != nil && c.Type == TextNode && strings.HasPrefix(c.Data, "\n") {
|
||||
switch n.Data {
|
||||
case "pre", "listing", "textarea":
|
||||
if err := w.WriteByte('\n'); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Render any child nodes.
|
||||
switch n.Data {
|
||||
case "iframe", "noembed", "noframes", "noscript", "plaintext", "script", "style", "xmp":
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
if c.Type == TextNode {
|
||||
if _, err := w.WriteString(c.Data); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := render1(w, c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if n.Data == "plaintext" {
|
||||
// Don't render anything else. <plaintext> must be the
|
||||
// last element in the file, with no closing tag.
|
||||
return plaintextAbort
|
||||
}
|
||||
default:
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
if err := render1(w, c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Render the </xxx> closing tag.
|
||||
if _, err := w.WriteString("</"); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.WriteString(n.Data); err != nil {
|
||||
return err
|
||||
}
|
||||
return w.WriteByte('>')
|
||||
}
|
||||
|
||||
// writeQuoted writes s to w surrounded by quotes. Normally it will use double
|
||||
// quotes, but if s contains a double quote, it will use single quotes.
|
||||
// It is used for writing the identifiers in a doctype declaration.
|
||||
// In valid HTML, they can't contain both types of quotes.
|
||||
func writeQuoted(w writer, s string) error {
|
||||
var q byte = '"'
|
||||
if strings.Contains(s, `"`) {
|
||||
q = '\''
|
||||
}
|
||||
if err := w.WriteByte(q); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.WriteString(s); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := w.WriteByte(q); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Section 12.1.2, "Elements", gives this list of void elements. Void elements
|
||||
// are those that can't have any contents.
|
||||
var voidElements = map[string]bool{
|
||||
"area": true,
|
||||
"base": true,
|
||||
"br": true,
|
||||
"col": true,
|
||||
"command": true,
|
||||
"embed": true,
|
||||
"hr": true,
|
||||
"img": true,
|
||||
"input": true,
|
||||
"keygen": true,
|
||||
"link": true,
|
||||
"meta": true,
|
||||
"param": true,
|
||||
"source": true,
|
||||
"track": true,
|
||||
"wbr": true,
|
||||
}
|
||||
156
vendor/golang.org/x/net/html/render_test.go
generated
vendored
Normal file
156
vendor/golang.org/x/net/html/render_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
// Copyright 2010 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 html
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRenderer(t *testing.T) {
|
||||
nodes := [...]*Node{
|
||||
0: {
|
||||
Type: ElementNode,
|
||||
Data: "html",
|
||||
},
|
||||
1: {
|
||||
Type: ElementNode,
|
||||
Data: "head",
|
||||
},
|
||||
2: {
|
||||
Type: ElementNode,
|
||||
Data: "body",
|
||||
},
|
||||
3: {
|
||||
Type: TextNode,
|
||||
Data: "0<1",
|
||||
},
|
||||
4: {
|
||||
Type: ElementNode,
|
||||
Data: "p",
|
||||
Attr: []Attribute{
|
||||
{
|
||||
Key: "id",
|
||||
Val: "A",
|
||||
},
|
||||
{
|
||||
Key: "foo",
|
||||
Val: `abc"def`,
|
||||
},
|
||||
},
|
||||
},
|
||||
5: {
|
||||
Type: TextNode,
|
||||
Data: "2",
|
||||
},
|
||||
6: {
|
||||
Type: ElementNode,
|
||||
Data: "b",
|
||||
Attr: []Attribute{
|
||||
{
|
||||
Key: "empty",
|
||||
Val: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
7: {
|
||||
Type: TextNode,
|
||||
Data: "3",
|
||||
},
|
||||
8: {
|
||||
Type: ElementNode,
|
||||
Data: "i",
|
||||
Attr: []Attribute{
|
||||
{
|
||||
Key: "backslash",
|
||||
Val: `\`,
|
||||
},
|
||||
},
|
||||
},
|
||||
9: {
|
||||
Type: TextNode,
|
||||
Data: "&4",
|
||||
},
|
||||
10: {
|
||||
Type: TextNode,
|
||||
Data: "5",
|
||||
},
|
||||
11: {
|
||||
Type: ElementNode,
|
||||
Data: "blockquote",
|
||||
},
|
||||
12: {
|
||||
Type: ElementNode,
|
||||
Data: "br",
|
||||
},
|
||||
13: {
|
||||
Type: TextNode,
|
||||
Data: "6",
|
||||
},
|
||||
}
|
||||
|
||||
// Build a tree out of those nodes, based on a textual representation.
|
||||
// Only the ".\t"s are significant. The trailing HTML-like text is
|
||||
// just commentary. The "0:" prefixes are for easy cross-reference with
|
||||
// the nodes array.
|
||||
treeAsText := [...]string{
|
||||
0: `<html>`,
|
||||
1: `. <head>`,
|
||||
2: `. <body>`,
|
||||
3: `. . "0<1"`,
|
||||
4: `. . <p id="A" foo="abc"def">`,
|
||||
5: `. . . "2"`,
|
||||
6: `. . . <b empty="">`,
|
||||
7: `. . . . "3"`,
|
||||
8: `. . . <i backslash="\">`,
|
||||
9: `. . . . "&4"`,
|
||||
10: `. . "5"`,
|
||||
11: `. . <blockquote>`,
|
||||
12: `. . <br>`,
|
||||
13: `. . "6"`,
|
||||
}
|
||||
if len(nodes) != len(treeAsText) {
|
||||
t.Fatal("len(nodes) != len(treeAsText)")
|
||||
}
|
||||
var stack [8]*Node
|
||||
for i, line := range treeAsText {
|
||||
level := 0
|
||||
for line[0] == '.' {
|
||||
// Strip a leading ".\t".
|
||||
line = line[2:]
|
||||
level++
|
||||
}
|
||||
n := nodes[i]
|
||||
if level == 0 {
|
||||
if stack[0] != nil {
|
||||
t.Fatal("multiple root nodes")
|
||||
}
|
||||
stack[0] = n
|
||||
} else {
|
||||
stack[level-1].AppendChild(n)
|
||||
stack[level] = n
|
||||
for i := level + 1; i < len(stack); i++ {
|
||||
stack[i] = nil
|
||||
}
|
||||
}
|
||||
// At each stage of tree construction, we check all nodes for consistency.
|
||||
for j, m := range nodes {
|
||||
if err := checkNodeConsistency(m); err != nil {
|
||||
t.Fatalf("i=%d, j=%d: %v", i, j, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
want := `<html><head></head><body>0<1<p id="A" foo="abc"def">` +
|
||||
`2<b empty="">3</b><i backslash="\">&4</i></p>` +
|
||||
`5<blockquote></blockquote><br/>6</body></html>`
|
||||
b := new(bytes.Buffer)
|
||||
if err := Render(b, nodes[0]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := b.String(); got != want {
|
||||
t.Errorf("got vs want:\n%s\n%s\n", got, want)
|
||||
}
|
||||
}
|
||||
1219
vendor/golang.org/x/net/html/token.go
generated
vendored
Normal file
1219
vendor/golang.org/x/net/html/token.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
748
vendor/golang.org/x/net/html/token_test.go
generated
vendored
Normal file
748
vendor/golang.org/x/net/html/token_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,748 @@
|
|||
// Copyright 2010 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 html
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type tokenTest struct {
|
||||
// A short description of the test case.
|
||||
desc string
|
||||
// The HTML to parse.
|
||||
html string
|
||||
// The string representations of the expected tokens, joined by '$'.
|
||||
golden string
|
||||
}
|
||||
|
||||
var tokenTests = []tokenTest{
|
||||
{
|
||||
"empty",
|
||||
"",
|
||||
"",
|
||||
},
|
||||
// A single text node. The tokenizer should not break text nodes on whitespace,
|
||||
// nor should it normalize whitespace within a text node.
|
||||
{
|
||||
"text",
|
||||
"foo bar",
|
||||
"foo bar",
|
||||
},
|
||||
// An entity.
|
||||
{
|
||||
"entity",
|
||||
"one < two",
|
||||
"one < two",
|
||||
},
|
||||
// A start, self-closing and end tag. The tokenizer does not care if the start
|
||||
// and end tokens don't match; that is the job of the parser.
|
||||
{
|
||||
"tags",
|
||||
"<a>b<c/>d</e>",
|
||||
"<a>$b$<c/>$d$</e>",
|
||||
},
|
||||
// Angle brackets that aren't a tag.
|
||||
{
|
||||
"not a tag #0",
|
||||
"<",
|
||||
"<",
|
||||
},
|
||||
{
|
||||
"not a tag #1",
|
||||
"</",
|
||||
"</",
|
||||
},
|
||||
{
|
||||
"not a tag #2",
|
||||
"</>",
|
||||
"<!---->",
|
||||
},
|
||||
{
|
||||
"not a tag #3",
|
||||
"a</>b",
|
||||
"a$<!---->$b",
|
||||
},
|
||||
{
|
||||
"not a tag #4",
|
||||
"</ >",
|
||||
"<!-- -->",
|
||||
},
|
||||
{
|
||||
"not a tag #5",
|
||||
"</.",
|
||||
"<!--.-->",
|
||||
},
|
||||
{
|
||||
"not a tag #6",
|
||||
"</.>",
|
||||
"<!--.-->",
|
||||
},
|
||||
{
|
||||
"not a tag #7",
|
||||
"a < b",
|
||||
"a < b",
|
||||
},
|
||||
{
|
||||
"not a tag #8",
|
||||
"<.>",
|
||||
"<.>",
|
||||
},
|
||||
{
|
||||
"not a tag #9",
|
||||
"a<<<b>>>c",
|
||||
"a<<$<b>$>>c",
|
||||
},
|
||||
{
|
||||
"not a tag #10",
|
||||
"if x<0 and y < 0 then x*y>0",
|
||||
"if x<0 and y < 0 then x*y>0",
|
||||
},
|
||||
{
|
||||
"not a tag #11",
|
||||
"<<p>",
|
||||
"<$<p>",
|
||||
},
|
||||
// EOF in a tag name.
|
||||
{
|
||||
"tag name eof #0",
|
||||
"<a",
|
||||
"",
|
||||
},
|
||||
{
|
||||
"tag name eof #1",
|
||||
"<a ",
|
||||
"",
|
||||
},
|
||||
{
|
||||
"tag name eof #2",
|
||||
"a<b",
|
||||
"a",
|
||||
},
|
||||
{
|
||||
"tag name eof #3",
|
||||
"<a><b",
|
||||
"<a>",
|
||||
},
|
||||
{
|
||||
"tag name eof #4",
|
||||
`<a x`,
|
||||
``,
|
||||
},
|
||||
// Some malformed tags that are missing a '>'.
|
||||
{
|
||||
"malformed tag #0",
|
||||
`<p</p>`,
|
||||
`<p< p="">`,
|
||||
},
|
||||
{
|
||||
"malformed tag #1",
|
||||
`<p </p>`,
|
||||
`<p <="" p="">`,
|
||||
},
|
||||
{
|
||||
"malformed tag #2",
|
||||
`<p id`,
|
||||
``,
|
||||
},
|
||||
{
|
||||
"malformed tag #3",
|
||||
`<p id=`,
|
||||
``,
|
||||
},
|
||||
{
|
||||
"malformed tag #4",
|
||||
`<p id=>`,
|
||||
`<p id="">`,
|
||||
},
|
||||
{
|
||||
"malformed tag #5",
|
||||
`<p id=0`,
|
||||
``,
|
||||
},
|
||||
{
|
||||
"malformed tag #6",
|
||||
`<p id=0</p>`,
|
||||
`<p id="0</p">`,
|
||||
},
|
||||
{
|
||||
"malformed tag #7",
|
||||
`<p id="0</p>`,
|
||||
``,
|
||||
},
|
||||
{
|
||||
"malformed tag #8",
|
||||
`<p id="0"</p>`,
|
||||
`<p id="0" <="" p="">`,
|
||||
},
|
||||
{
|
||||
"malformed tag #9",
|
||||
`<p></p id`,
|
||||
`<p>`,
|
||||
},
|
||||
// Raw text and RCDATA.
|
||||
{
|
||||
"basic raw text",
|
||||
"<script><a></b></script>",
|
||||
"<script>$<a></b>$</script>",
|
||||
},
|
||||
{
|
||||
"unfinished script end tag",
|
||||
"<SCRIPT>a</SCR",
|
||||
"<script>$a</SCR",
|
||||
},
|
||||
{
|
||||
"broken script end tag",
|
||||
"<SCRIPT>a</SCR ipt>",
|
||||
"<script>$a</SCR ipt>",
|
||||
},
|
||||
{
|
||||
"EOF in script end tag",
|
||||
"<SCRIPT>a</SCRipt",
|
||||
"<script>$a</SCRipt",
|
||||
},
|
||||
{
|
||||
"scriptx end tag",
|
||||
"<SCRIPT>a</SCRiptx",
|
||||
"<script>$a</SCRiptx",
|
||||
},
|
||||
{
|
||||
"' ' completes script end tag",
|
||||
"<SCRIPT>a</SCRipt ",
|
||||
"<script>$a",
|
||||
},
|
||||
{
|
||||
"'>' completes script end tag",
|
||||
"<SCRIPT>a</SCRipt>",
|
||||
"<script>$a$</script>",
|
||||
},
|
||||
{
|
||||
"self-closing script end tag",
|
||||
"<SCRIPT>a</SCRipt/>",
|
||||
"<script>$a$</script>",
|
||||
},
|
||||
{
|
||||
"nested script tag",
|
||||
"<SCRIPT>a</SCRipt<script>",
|
||||
"<script>$a</SCRipt<script>",
|
||||
},
|
||||
{
|
||||
"script end tag after unfinished",
|
||||
"<SCRIPT>a</SCRipt</script>",
|
||||
"<script>$a</SCRipt$</script>",
|
||||
},
|
||||
{
|
||||
"script/style mismatched tags",
|
||||
"<script>a</style>",
|
||||
"<script>$a</style>",
|
||||
},
|
||||
{
|
||||
"style element with entity",
|
||||
"<style>'",
|
||||
"<style>$&apos;",
|
||||
},
|
||||
{
|
||||
"textarea with tag",
|
||||
"<textarea><div></textarea>",
|
||||
"<textarea>$<div>$</textarea>",
|
||||
},
|
||||
{
|
||||
"title with tag and entity",
|
||||
"<title><b>K&R C</b></title>",
|
||||
"<title>$<b>K&R C</b>$</title>",
|
||||
},
|
||||
// DOCTYPE tests.
|
||||
{
|
||||
"Proper DOCTYPE",
|
||||
"<!DOCTYPE html>",
|
||||
"<!DOCTYPE html>",
|
||||
},
|
||||
{
|
||||
"DOCTYPE with no space",
|
||||
"<!doctypehtml>",
|
||||
"<!DOCTYPE html>",
|
||||
},
|
||||
{
|
||||
"DOCTYPE with two spaces",
|
||||
"<!doctype html>",
|
||||
"<!DOCTYPE html>",
|
||||
},
|
||||
{
|
||||
"looks like DOCTYPE but isn't",
|
||||
"<!DOCUMENT html>",
|
||||
"<!--DOCUMENT html-->",
|
||||
},
|
||||
{
|
||||
"DOCTYPE at EOF",
|
||||
"<!DOCtype",
|
||||
"<!DOCTYPE >",
|
||||
},
|
||||
// XML processing instructions.
|
||||
{
|
||||
"XML processing instruction",
|
||||
"<?xml?>",
|
||||
"<!--?xml?-->",
|
||||
},
|
||||
// Comments.
|
||||
{
|
||||
"comment0",
|
||||
"abc<b><!-- skipme --></b>def",
|
||||
"abc$<b>$<!-- skipme -->$</b>$def",
|
||||
},
|
||||
{
|
||||
"comment1",
|
||||
"a<!-->z",
|
||||
"a$<!---->$z",
|
||||
},
|
||||
{
|
||||
"comment2",
|
||||
"a<!--->z",
|
||||
"a$<!---->$z",
|
||||
},
|
||||
{
|
||||
"comment3",
|
||||
"a<!--x>-->z",
|
||||
"a$<!--x>-->$z",
|
||||
},
|
||||
{
|
||||
"comment4",
|
||||
"a<!--x->-->z",
|
||||
"a$<!--x->-->$z",
|
||||
},
|
||||
{
|
||||
"comment5",
|
||||
"a<!>z",
|
||||
"a$<!---->$z",
|
||||
},
|
||||
{
|
||||
"comment6",
|
||||
"a<!->z",
|
||||
"a$<!----->$z",
|
||||
},
|
||||
{
|
||||
"comment7",
|
||||
"a<!---<>z",
|
||||
"a$<!---<>z-->",
|
||||
},
|
||||
{
|
||||
"comment8",
|
||||
"a<!--z",
|
||||
"a$<!--z-->",
|
||||
},
|
||||
{
|
||||
"comment9",
|
||||
"a<!--z-",
|
||||
"a$<!--z-->",
|
||||
},
|
||||
{
|
||||
"comment10",
|
||||
"a<!--z--",
|
||||
"a$<!--z-->",
|
||||
},
|
||||
{
|
||||
"comment11",
|
||||
"a<!--z---",
|
||||
"a$<!--z--->",
|
||||
},
|
||||
{
|
||||
"comment12",
|
||||
"a<!--z----",
|
||||
"a$<!--z---->",
|
||||
},
|
||||
{
|
||||
"comment13",
|
||||
"a<!--x--!>z",
|
||||
"a$<!--x-->$z",
|
||||
},
|
||||
// An attribute with a backslash.
|
||||
{
|
||||
"backslash",
|
||||
`<p id="a\"b">`,
|
||||
`<p id="a\" b"="">`,
|
||||
},
|
||||
// Entities, tag name and attribute key lower-casing, and whitespace
|
||||
// normalization within a tag.
|
||||
{
|
||||
"tricky",
|
||||
"<p \t\n iD=\"a"B\" foo=\"bar\"><EM>te<&;xt</em></p>",
|
||||
`<p id="a"B" foo="bar">$<em>$te<&;xt$</em>$</p>`,
|
||||
},
|
||||
// A nonexistent entity. Tokenizing and converting back to a string should
|
||||
// escape the "&" to become "&".
|
||||
{
|
||||
"noSuchEntity",
|
||||
`<a b="c&noSuchEntity;d"><&alsoDoesntExist;&`,
|
||||
`<a b="c&noSuchEntity;d">$<&alsoDoesntExist;&`,
|
||||
},
|
||||
{
|
||||
"entity without semicolon",
|
||||
`¬it;∉<a b="q=z&=5¬ice=hello¬=world">`,
|
||||
`¬it;∉$<a b="q=z&amp=5&notice=hello¬=world">`,
|
||||
},
|
||||
{
|
||||
"entity with digits",
|
||||
"½",
|
||||
"½",
|
||||
},
|
||||
// Attribute tests:
|
||||
// http://dev.w3.org/html5/pf-summary/Overview.html#attributes
|
||||
{
|
||||
"Empty attribute",
|
||||
`<input disabled FOO>`,
|
||||
`<input disabled="" foo="">`,
|
||||
},
|
||||
{
|
||||
"Empty attribute, whitespace",
|
||||
`<input disabled FOO >`,
|
||||
`<input disabled="" foo="">`,
|
||||
},
|
||||
{
|
||||
"Unquoted attribute value",
|
||||
`<input value=yes FOO=BAR>`,
|
||||
`<input value="yes" foo="BAR">`,
|
||||
},
|
||||
{
|
||||
"Unquoted attribute value, spaces",
|
||||
`<input value = yes FOO = BAR>`,
|
||||
`<input value="yes" foo="BAR">`,
|
||||
},
|
||||
{
|
||||
"Unquoted attribute value, trailing space",
|
||||
`<input value=yes FOO=BAR >`,
|
||||
`<input value="yes" foo="BAR">`,
|
||||
},
|
||||
{
|
||||
"Single-quoted attribute value",
|
||||
`<input value='yes' FOO='BAR'>`,
|
||||
`<input value="yes" foo="BAR">`,
|
||||
},
|
||||
{
|
||||
"Single-quoted attribute value, trailing space",
|
||||
`<input value='yes' FOO='BAR' >`,
|
||||
`<input value="yes" foo="BAR">`,
|
||||
},
|
||||
{
|
||||
"Double-quoted attribute value",
|
||||
`<input value="I'm an attribute" FOO="BAR">`,
|
||||
`<input value="I'm an attribute" foo="BAR">`,
|
||||
},
|
||||
{
|
||||
"Attribute name characters",
|
||||
`<meta http-equiv="content-type">`,
|
||||
`<meta http-equiv="content-type">`,
|
||||
},
|
||||
{
|
||||
"Mixed attributes",
|
||||
`a<P V="0 1" w='2' X=3 y>z`,
|
||||
`a$<p v="0 1" w="2" x="3" y="">$z`,
|
||||
},
|
||||
{
|
||||
"Attributes with a solitary single quote",
|
||||
`<p id=can't><p id=won't>`,
|
||||
`<p id="can't">$<p id="won't">`,
|
||||
},
|
||||
}
|
||||
|
||||
func TestTokenizer(t *testing.T) {
|
||||
loop:
|
||||
for _, tt := range tokenTests {
|
||||
z := NewTokenizer(strings.NewReader(tt.html))
|
||||
if tt.golden != "" {
|
||||
for i, s := range strings.Split(tt.golden, "$") {
|
||||
if z.Next() == ErrorToken {
|
||||
t.Errorf("%s token %d: want %q got error %v", tt.desc, i, s, z.Err())
|
||||
continue loop
|
||||
}
|
||||
actual := z.Token().String()
|
||||
if s != actual {
|
||||
t.Errorf("%s token %d: want %q got %q", tt.desc, i, s, actual)
|
||||
continue loop
|
||||
}
|
||||
}
|
||||
}
|
||||
z.Next()
|
||||
if z.Err() != io.EOF {
|
||||
t.Errorf("%s: want EOF got %q", tt.desc, z.Err())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaxBuffer(t *testing.T) {
|
||||
// Exceeding the maximum buffer size generates ErrBufferExceeded.
|
||||
z := NewTokenizer(strings.NewReader("<" + strings.Repeat("t", 10)))
|
||||
z.SetMaxBuf(5)
|
||||
tt := z.Next()
|
||||
if got, want := tt, ErrorToken; got != want {
|
||||
t.Fatalf("token type: got: %v want: %v", got, want)
|
||||
}
|
||||
if got, want := z.Err(), ErrBufferExceeded; got != want {
|
||||
t.Errorf("error type: got: %v want: %v", got, want)
|
||||
}
|
||||
if got, want := string(z.Raw()), "<tttt"; got != want {
|
||||
t.Fatalf("buffered before overflow: got: %q want: %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaxBufferReconstruction(t *testing.T) {
|
||||
// Exceeding the maximum buffer size at any point while tokenizing permits
|
||||
// reconstructing the original input.
|
||||
tests:
|
||||
for _, test := range tokenTests {
|
||||
for maxBuf := 1; ; maxBuf++ {
|
||||
r := strings.NewReader(test.html)
|
||||
z := NewTokenizer(r)
|
||||
z.SetMaxBuf(maxBuf)
|
||||
var tokenized bytes.Buffer
|
||||
for {
|
||||
tt := z.Next()
|
||||
tokenized.Write(z.Raw())
|
||||
if tt == ErrorToken {
|
||||
if err := z.Err(); err != io.EOF && err != ErrBufferExceeded {
|
||||
t.Errorf("%s: unexpected error: %v", test.desc, err)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
// Anything tokenized along with untokenized input or data left in the reader.
|
||||
assembled, err := ioutil.ReadAll(io.MultiReader(&tokenized, bytes.NewReader(z.Buffered()), r))
|
||||
if err != nil {
|
||||
t.Errorf("%s: ReadAll: %v", test.desc, err)
|
||||
continue tests
|
||||
}
|
||||
if got, want := string(assembled), test.html; got != want {
|
||||
t.Errorf("%s: reassembled html:\n got: %q\nwant: %q", test.desc, got, want)
|
||||
continue tests
|
||||
}
|
||||
// EOF indicates that we completed tokenization and hence found the max
|
||||
// maxBuf that generates ErrBufferExceeded, so continue to the next test.
|
||||
if z.Err() == io.EOF {
|
||||
break
|
||||
}
|
||||
} // buffer sizes
|
||||
} // tests
|
||||
}
|
||||
|
||||
func TestPassthrough(t *testing.T) {
|
||||
// Accumulating the raw output for each parse event should reconstruct the
|
||||
// original input.
|
||||
for _, test := range tokenTests {
|
||||
z := NewTokenizer(strings.NewReader(test.html))
|
||||
var parsed bytes.Buffer
|
||||
for {
|
||||
tt := z.Next()
|
||||
parsed.Write(z.Raw())
|
||||
if tt == ErrorToken {
|
||||
break
|
||||
}
|
||||
}
|
||||
if got, want := parsed.String(), test.html; got != want {
|
||||
t.Errorf("%s: parsed output:\n got: %q\nwant: %q", test.desc, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBufAPI(t *testing.T) {
|
||||
s := "0<a>1</a>2<b>3<a>4<a>5</a>6</b>7</a>8<a/>9"
|
||||
z := NewTokenizer(bytes.NewBufferString(s))
|
||||
var result bytes.Buffer
|
||||
depth := 0
|
||||
loop:
|
||||
for {
|
||||
tt := z.Next()
|
||||
switch tt {
|
||||
case ErrorToken:
|
||||
if z.Err() != io.EOF {
|
||||
t.Error(z.Err())
|
||||
}
|
||||
break loop
|
||||
case TextToken:
|
||||
if depth > 0 {
|
||||
result.Write(z.Text())
|
||||
}
|
||||
case StartTagToken, EndTagToken:
|
||||
tn, _ := z.TagName()
|
||||
if len(tn) == 1 && tn[0] == 'a' {
|
||||
if tt == StartTagToken {
|
||||
depth++
|
||||
} else {
|
||||
depth--
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
u := "14567"
|
||||
v := string(result.Bytes())
|
||||
if u != v {
|
||||
t.Errorf("TestBufAPI: want %q got %q", u, v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertNewlines(t *testing.T) {
|
||||
testCases := map[string]string{
|
||||
"Mac\rDOS\r\nUnix\n": "Mac\nDOS\nUnix\n",
|
||||
"Unix\nMac\rDOS\r\n": "Unix\nMac\nDOS\n",
|
||||
"DOS\r\nDOS\r\nDOS\r\n": "DOS\nDOS\nDOS\n",
|
||||
"": "",
|
||||
"\n": "\n",
|
||||
"\n\r": "\n\n",
|
||||
"\r": "\n",
|
||||
"\r\n": "\n",
|
||||
"\r\n\n": "\n\n",
|
||||
"\r\n\r": "\n\n",
|
||||
"\r\n\r\n": "\n\n",
|
||||
"\r\r": "\n\n",
|
||||
"\r\r\n": "\n\n",
|
||||
"\r\r\n\n": "\n\n\n",
|
||||
"\r\r\r\n": "\n\n\n",
|
||||
"\r \n": "\n \n",
|
||||
"xyz": "xyz",
|
||||
}
|
||||
for in, want := range testCases {
|
||||
if got := string(convertNewlines([]byte(in))); got != want {
|
||||
t.Errorf("input %q: got %q, want %q", in, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReaderEdgeCases(t *testing.T) {
|
||||
const s = "<p>An io.Reader can return (0, nil) or (n, io.EOF).</p>"
|
||||
testCases := []io.Reader{
|
||||
&zeroOneByteReader{s: s},
|
||||
&eofStringsReader{s: s},
|
||||
&stuckReader{},
|
||||
}
|
||||
for i, tc := range testCases {
|
||||
got := []TokenType{}
|
||||
z := NewTokenizer(tc)
|
||||
for {
|
||||
tt := z.Next()
|
||||
if tt == ErrorToken {
|
||||
break
|
||||
}
|
||||
got = append(got, tt)
|
||||
}
|
||||
if err := z.Err(); err != nil && err != io.EOF {
|
||||
if err != io.ErrNoProgress {
|
||||
t.Errorf("i=%d: %v", i, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
want := []TokenType{
|
||||
StartTagToken,
|
||||
TextToken,
|
||||
EndTagToken,
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("i=%d: got %v, want %v", i, got, want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// zeroOneByteReader is like a strings.Reader that alternates between
|
||||
// returning 0 bytes and 1 byte at a time.
|
||||
type zeroOneByteReader struct {
|
||||
s string
|
||||
n int
|
||||
}
|
||||
|
||||
func (r *zeroOneByteReader) Read(p []byte) (int, error) {
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
if len(r.s) == 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
r.n++
|
||||
if r.n%2 != 0 {
|
||||
return 0, nil
|
||||
}
|
||||
p[0], r.s = r.s[0], r.s[1:]
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
// eofStringsReader is like a strings.Reader but can return an (n, err) where
|
||||
// n > 0 && err != nil.
|
||||
type eofStringsReader struct {
|
||||
s string
|
||||
}
|
||||
|
||||
func (r *eofStringsReader) Read(p []byte) (int, error) {
|
||||
n := copy(p, r.s)
|
||||
r.s = r.s[n:]
|
||||
if r.s != "" {
|
||||
return n, nil
|
||||
}
|
||||
return n, io.EOF
|
||||
}
|
||||
|
||||
// stuckReader is an io.Reader that always returns no data and no error.
|
||||
type stuckReader struct{}
|
||||
|
||||
func (*stuckReader) Read(p []byte) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
const (
|
||||
rawLevel = iota
|
||||
lowLevel
|
||||
highLevel
|
||||
)
|
||||
|
||||
func benchmarkTokenizer(b *testing.B, level int) {
|
||||
buf, err := ioutil.ReadFile("testdata/go1.html")
|
||||
if err != nil {
|
||||
b.Fatalf("could not read testdata/go1.html: %v", err)
|
||||
}
|
||||
b.SetBytes(int64(len(buf)))
|
||||
runtime.GC()
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
z := NewTokenizer(bytes.NewBuffer(buf))
|
||||
for {
|
||||
tt := z.Next()
|
||||
if tt == ErrorToken {
|
||||
if err := z.Err(); err != nil && err != io.EOF {
|
||||
b.Fatalf("tokenizer error: %v", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
switch level {
|
||||
case rawLevel:
|
||||
// Calling z.Raw just returns the raw bytes of the token. It does
|
||||
// not unescape < to <, or lower-case tag names and attribute keys.
|
||||
z.Raw()
|
||||
case lowLevel:
|
||||
// Caling z.Text, z.TagName and z.TagAttr returns []byte values
|
||||
// whose contents may change on the next call to z.Next.
|
||||
switch tt {
|
||||
case TextToken, CommentToken, DoctypeToken:
|
||||
z.Text()
|
||||
case StartTagToken, SelfClosingTagToken:
|
||||
_, more := z.TagName()
|
||||
for more {
|
||||
_, _, more = z.TagAttr()
|
||||
}
|
||||
case EndTagToken:
|
||||
z.TagName()
|
||||
}
|
||||
case highLevel:
|
||||
// Calling z.Token converts []byte values to strings whose validity
|
||||
// extend beyond the next call to z.Next.
|
||||
z.Token()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRawLevelTokenizer(b *testing.B) { benchmarkTokenizer(b, rawLevel) }
|
||||
func BenchmarkLowLevelTokenizer(b *testing.B) { benchmarkTokenizer(b, lowLevel) }
|
||||
func BenchmarkHighLevelTokenizer(b *testing.B) { benchmarkTokenizer(b, highLevel) }
|
||||
42
vendor/golang.org/x/net/http2/server.go
generated
vendored
42
vendor/golang.org/x/net/http2/server.go
generated
vendored
|
|
@ -853,8 +853,13 @@ func (sc *serverConn) serve() {
|
|||
}
|
||||
}
|
||||
|
||||
if sc.inGoAway && sc.curOpenStreams() == 0 && !sc.needToSendGoAway && !sc.writingFrame {
|
||||
return
|
||||
// Start the shutdown timer after sending a GOAWAY. When sending GOAWAY
|
||||
// with no error code (graceful shutdown), don't start the timer until
|
||||
// all open streams have been completed.
|
||||
sentGoAway := sc.inGoAway && !sc.needToSendGoAway && !sc.writingFrame
|
||||
gracefulShutdownComplete := sc.goAwayCode == ErrCodeNo && sc.curOpenStreams() == 0
|
||||
if sentGoAway && sc.shutdownTimer == nil && (sc.goAwayCode != ErrCodeNo || gracefulShutdownComplete) {
|
||||
sc.shutDownIn(goAwayTimeout)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1218,30 +1223,31 @@ func (sc *serverConn) startGracefulShutdown() {
|
|||
sc.shutdownOnce.Do(func() { sc.sendServeMsg(gracefulShutdownMsg) })
|
||||
}
|
||||
|
||||
// After sending GOAWAY, the connection will close after goAwayTimeout.
|
||||
// If we close the connection immediately after sending GOAWAY, there may
|
||||
// be unsent data in our kernel receive buffer, which will cause the kernel
|
||||
// to send a TCP RST on close() instead of a FIN. This RST will abort the
|
||||
// connection immediately, whether or not the client had received the GOAWAY.
|
||||
//
|
||||
// Ideally we should delay for at least 1 RTT + epsilon so the client has
|
||||
// a chance to read the GOAWAY and stop sending messages. Measuring RTT
|
||||
// is hard, so we approximate with 1 second. See golang.org/issue/18701.
|
||||
//
|
||||
// This is a var so it can be shorter in tests, where all requests uses the
|
||||
// loopback interface making the expected RTT very small.
|
||||
//
|
||||
// TODO: configurable?
|
||||
var goAwayTimeout = 1 * time.Second
|
||||
|
||||
func (sc *serverConn) startGracefulShutdownInternal() {
|
||||
sc.goAwayIn(ErrCodeNo, 0)
|
||||
sc.goAway(ErrCodeNo)
|
||||
}
|
||||
|
||||
func (sc *serverConn) goAway(code ErrCode) {
|
||||
sc.serveG.check()
|
||||
var forceCloseIn time.Duration
|
||||
if code != ErrCodeNo {
|
||||
forceCloseIn = 250 * time.Millisecond
|
||||
} else {
|
||||
// TODO: configurable
|
||||
forceCloseIn = 1 * time.Second
|
||||
}
|
||||
sc.goAwayIn(code, forceCloseIn)
|
||||
}
|
||||
|
||||
func (sc *serverConn) goAwayIn(code ErrCode, forceCloseIn time.Duration) {
|
||||
sc.serveG.check()
|
||||
if sc.inGoAway {
|
||||
return
|
||||
}
|
||||
if forceCloseIn != 0 {
|
||||
sc.shutDownIn(forceCloseIn)
|
||||
}
|
||||
sc.inGoAway = true
|
||||
sc.needToSendGoAway = true
|
||||
sc.goAwayCode = code
|
||||
|
|
|
|||
1
vendor/golang.org/x/net/http2/server_test.go
generated
vendored
1
vendor/golang.org/x/net/http2/server_test.go
generated
vendored
|
|
@ -68,6 +68,7 @@ type serverTester struct {
|
|||
|
||||
func init() {
|
||||
testHookOnPanicMu = new(sync.Mutex)
|
||||
goAwayTimeout = 25 * time.Millisecond
|
||||
}
|
||||
|
||||
func resetHooks() {
|
||||
|
|
|
|||
12
vendor/golang.org/x/net/http2/transport.go
generated
vendored
12
vendor/golang.org/x/net/http2/transport.go
generated
vendored
|
|
@ -1536,7 +1536,17 @@ func (rl *clientConnReadLoop) run() error {
|
|||
|
||||
func (rl *clientConnReadLoop) processHeaders(f *MetaHeadersFrame) error {
|
||||
cc := rl.cc
|
||||
cs := cc.streamByID(f.StreamID, f.StreamEnded())
|
||||
if f.StreamEnded() {
|
||||
// Issue 20521: If the stream has ended, streamByID() causes
|
||||
// clientStream.done to be closed, which causes the request's bodyWriter
|
||||
// to be closed with an errStreamClosed, which may be received by
|
||||
// clientConn.RoundTrip before the result of processing these headers.
|
||||
// Deferring stream closure allows the header processing to occur first.
|
||||
// clientConn.RoundTrip may still receive the bodyWriter error first, but
|
||||
// the fix for issue 16102 prioritises any response.
|
||||
defer cc.streamByID(f.StreamID, true)
|
||||
}
|
||||
cs := cc.streamByID(f.StreamID, false)
|
||||
if cs == nil {
|
||||
// We'd get here if we canceled a request while the
|
||||
// server had its response still in flight. So if this
|
||||
|
|
|
|||
28
vendor/golang.org/x/net/http2/transport_test.go
generated
vendored
28
vendor/golang.org/x/net/http2/transport_test.go
generated
vendored
|
|
@ -3678,6 +3678,34 @@ func benchSimpleRoundTrip(b *testing.B, nHeaders int) {
|
|||
}
|
||||
}
|
||||
|
||||
type infiniteReader struct{}
|
||||
|
||||
func (r infiniteReader) Read(b []byte) (int, error) {
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
// Issue 20521: it is not an error to receive a response and end stream
|
||||
// from the server without the body being consumed.
|
||||
func TestTransportResponseAndResetWithoutConsumingBodyRace(t *testing.T) {
|
||||
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}, optOnlyServer)
|
||||
defer st.Close()
|
||||
|
||||
tr := &Transport{TLSClientConfig: tlsConfigInsecure}
|
||||
defer tr.CloseIdleConnections()
|
||||
|
||||
// The request body needs to be big enough to trigger flow control.
|
||||
req, _ := http.NewRequest("PUT", st.ts.URL, infiniteReader{})
|
||||
res, err := tr.RoundTrip(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if res.StatusCode != http.StatusOK {
|
||||
t.Fatalf("Response code = %v; want %v", res.StatusCode, http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkClientRequestHeaders(b *testing.B) {
|
||||
b.Run(" 0 Headers", func(b *testing.B) { benchSimpleRoundTrip(b, 0) })
|
||||
b.Run(" 10 Headers", func(b *testing.B) { benchSimpleRoundTrip(b, 10) })
|
||||
|
|
|
|||
7
vendor/golang.org/x/net/http2/write.go
generated
vendored
7
vendor/golang.org/x/net/http2/write.go
generated
vendored
|
|
@ -10,7 +10,6 @@ import (
|
|||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/http2/hpack"
|
||||
"golang.org/x/net/lex/httplex"
|
||||
|
|
@ -90,11 +89,7 @@ type writeGoAway struct {
|
|||
|
||||
func (p *writeGoAway) writeFrame(ctx writeContext) error {
|
||||
err := ctx.Framer().WriteGoAway(p.maxStreamID, p.code, nil)
|
||||
if p.code != 0 {
|
||||
ctx.Flush() // ignore error: we're hanging up on them anyway
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
ctx.CloseConn()
|
||||
}
|
||||
ctx.Flush() // ignore error: we're hanging up on them anyway
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
|||
713
vendor/golang.org/x/net/publicsuffix/gen.go
generated
vendored
Normal file
713
vendor/golang.org/x/net/publicsuffix/gen.go
generated
vendored
Normal file
|
|
@ -0,0 +1,713 @@
|
|||
// Copyright 2012 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 ignore
|
||||
|
||||
package main
|
||||
|
||||
// This program generates table.go and table_test.go based on the authoritative
|
||||
// public suffix list at https://publicsuffix.org/list/effective_tld_names.dat
|
||||
//
|
||||
// The version is derived from
|
||||
// https://api.github.com/repos/publicsuffix/list/commits?path=public_suffix_list.dat
|
||||
// and a human-readable form is at
|
||||
// https://github.com/publicsuffix/list/commits/master/public_suffix_list.dat
|
||||
//
|
||||
// To fetch a particular git revision, such as 5c70ccd250, pass
|
||||
// -url "https://raw.githubusercontent.com/publicsuffix/list/5c70ccd250/public_suffix_list.dat"
|
||||
// and -version "an explicit version string".
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/idna"
|
||||
)
|
||||
|
||||
const (
|
||||
// These sum of these four values must be no greater than 32.
|
||||
nodesBitsChildren = 10
|
||||
nodesBitsICANN = 1
|
||||
nodesBitsTextOffset = 15
|
||||
nodesBitsTextLength = 6
|
||||
|
||||
// These sum of these four values must be no greater than 32.
|
||||
childrenBitsWildcard = 1
|
||||
childrenBitsNodeType = 2
|
||||
childrenBitsHi = 14
|
||||
childrenBitsLo = 14
|
||||
)
|
||||
|
||||
var (
|
||||
maxChildren int
|
||||
maxTextOffset int
|
||||
maxTextLength int
|
||||
maxHi uint32
|
||||
maxLo uint32
|
||||
)
|
||||
|
||||
func max(a, b int) int {
|
||||
if a < b {
|
||||
return b
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func u32max(a, b uint32) uint32 {
|
||||
if a < b {
|
||||
return b
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
const (
|
||||
nodeTypeNormal = 0
|
||||
nodeTypeException = 1
|
||||
nodeTypeParentOnly = 2
|
||||
numNodeType = 3
|
||||
)
|
||||
|
||||
func nodeTypeStr(n int) string {
|
||||
switch n {
|
||||
case nodeTypeNormal:
|
||||
return "+"
|
||||
case nodeTypeException:
|
||||
return "!"
|
||||
case nodeTypeParentOnly:
|
||||
return "o"
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
const (
|
||||
defaultURL = "https://publicsuffix.org/list/effective_tld_names.dat"
|
||||
gitCommitURL = "https://api.github.com/repos/publicsuffix/list/commits?path=public_suffix_list.dat"
|
||||
)
|
||||
|
||||
var (
|
||||
labelEncoding = map[string]uint32{}
|
||||
labelsList = []string{}
|
||||
labelsMap = map[string]bool{}
|
||||
rules = []string{}
|
||||
|
||||
// validSuffixRE is used to check that the entries in the public suffix
|
||||
// list are in canonical form (after Punycode encoding). Specifically,
|
||||
// capital letters are not allowed.
|
||||
validSuffixRE = regexp.MustCompile(`^[a-z0-9_\!\*\-\.]+$`)
|
||||
|
||||
shaRE = regexp.MustCompile(`"sha":"([^"]+)"`)
|
||||
dateRE = regexp.MustCompile(`"committer":{[^{]+"date":"([^"]+)"`)
|
||||
|
||||
comments = flag.Bool("comments", false, "generate table.go comments, for debugging")
|
||||
subset = flag.Bool("subset", false, "generate only a subset of the full table, for debugging")
|
||||
url = flag.String("url", defaultURL, "URL of the publicsuffix.org list. If empty, stdin is read instead")
|
||||
v = flag.Bool("v", false, "verbose output (to stderr)")
|
||||
version = flag.String("version", "", "the effective_tld_names.dat version")
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := main1(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func main1() error {
|
||||
flag.Parse()
|
||||
if nodesBitsTextLength+nodesBitsTextOffset+nodesBitsICANN+nodesBitsChildren > 32 {
|
||||
return fmt.Errorf("not enough bits to encode the nodes table")
|
||||
}
|
||||
if childrenBitsLo+childrenBitsHi+childrenBitsNodeType+childrenBitsWildcard > 32 {
|
||||
return fmt.Errorf("not enough bits to encode the children table")
|
||||
}
|
||||
if *version == "" {
|
||||
if *url != defaultURL {
|
||||
return fmt.Errorf("-version was not specified, and the -url is not the default one")
|
||||
}
|
||||
sha, date, err := gitCommit()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*version = fmt.Sprintf("publicsuffix.org's public_suffix_list.dat, git revision %s (%s)", sha, date)
|
||||
}
|
||||
var r io.Reader = os.Stdin
|
||||
if *url != "" {
|
||||
res, err := http.Get(*url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("bad GET status for %s: %d", *url, res.Status)
|
||||
}
|
||||
r = res.Body
|
||||
defer res.Body.Close()
|
||||
}
|
||||
|
||||
var root node
|
||||
icann := false
|
||||
br := bufio.NewReader(r)
|
||||
for {
|
||||
s, err := br.ReadString('\n')
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return err
|
||||
}
|
||||
s = strings.TrimSpace(s)
|
||||
if strings.Contains(s, "BEGIN ICANN DOMAINS") {
|
||||
icann = true
|
||||
continue
|
||||
}
|
||||
if strings.Contains(s, "END ICANN DOMAINS") {
|
||||
icann = false
|
||||
continue
|
||||
}
|
||||
if s == "" || strings.HasPrefix(s, "//") {
|
||||
continue
|
||||
}
|
||||
s, err = idna.ToASCII(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !validSuffixRE.MatchString(s) {
|
||||
return fmt.Errorf("bad publicsuffix.org list data: %q", s)
|
||||
}
|
||||
|
||||
if *subset {
|
||||
switch {
|
||||
case s == "ac.jp" || strings.HasSuffix(s, ".ac.jp"):
|
||||
case s == "ak.us" || strings.HasSuffix(s, ".ak.us"):
|
||||
case s == "ao" || strings.HasSuffix(s, ".ao"):
|
||||
case s == "ar" || strings.HasSuffix(s, ".ar"):
|
||||
case s == "arpa" || strings.HasSuffix(s, ".arpa"):
|
||||
case s == "cy" || strings.HasSuffix(s, ".cy"):
|
||||
case s == "dyndns.org" || strings.HasSuffix(s, ".dyndns.org"):
|
||||
case s == "jp":
|
||||
case s == "kobe.jp" || strings.HasSuffix(s, ".kobe.jp"):
|
||||
case s == "kyoto.jp" || strings.HasSuffix(s, ".kyoto.jp"):
|
||||
case s == "om" || strings.HasSuffix(s, ".om"):
|
||||
case s == "uk" || strings.HasSuffix(s, ".uk"):
|
||||
case s == "uk.com" || strings.HasSuffix(s, ".uk.com"):
|
||||
case s == "tw" || strings.HasSuffix(s, ".tw"):
|
||||
case s == "zw" || strings.HasSuffix(s, ".zw"):
|
||||
case s == "xn--p1ai" || strings.HasSuffix(s, ".xn--p1ai"):
|
||||
// xn--p1ai is Russian-Cyrillic "рф".
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
rules = append(rules, s)
|
||||
|
||||
nt, wildcard := nodeTypeNormal, false
|
||||
switch {
|
||||
case strings.HasPrefix(s, "*."):
|
||||
s, nt = s[2:], nodeTypeParentOnly
|
||||
wildcard = true
|
||||
case strings.HasPrefix(s, "!"):
|
||||
s, nt = s[1:], nodeTypeException
|
||||
}
|
||||
labels := strings.Split(s, ".")
|
||||
for n, i := &root, len(labels)-1; i >= 0; i-- {
|
||||
label := labels[i]
|
||||
n = n.child(label)
|
||||
if i == 0 {
|
||||
if nt != nodeTypeParentOnly && n.nodeType == nodeTypeParentOnly {
|
||||
n.nodeType = nt
|
||||
}
|
||||
n.icann = n.icann && icann
|
||||
n.wildcard = n.wildcard || wildcard
|
||||
}
|
||||
labelsMap[label] = true
|
||||
}
|
||||
}
|
||||
labelsList = make([]string, 0, len(labelsMap))
|
||||
for label := range labelsMap {
|
||||
labelsList = append(labelsList, label)
|
||||
}
|
||||
sort.Strings(labelsList)
|
||||
|
||||
if err := generate(printReal, &root, "table.go"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := generate(printTest, &root, "table_test.go"); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func generate(p func(io.Writer, *node) error, root *node, filename string) error {
|
||||
buf := new(bytes.Buffer)
|
||||
if err := p(buf, root); err != nil {
|
||||
return err
|
||||
}
|
||||
b, err := format.Source(buf.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(filename, b, 0644)
|
||||
}
|
||||
|
||||
func gitCommit() (sha, date string, retErr error) {
|
||||
res, err := http.Get(gitCommitURL)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return "", "", fmt.Errorf("bad GET status for %s: %d", gitCommitURL, res.Status)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
b, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if m := shaRE.FindSubmatch(b); m != nil {
|
||||
sha = string(m[1])
|
||||
}
|
||||
if m := dateRE.FindSubmatch(b); m != nil {
|
||||
date = string(m[1])
|
||||
}
|
||||
if sha == "" || date == "" {
|
||||
retErr = fmt.Errorf("could not find commit SHA and date in %s", gitCommitURL)
|
||||
}
|
||||
return sha, date, retErr
|
||||
}
|
||||
|
||||
func printTest(w io.Writer, n *node) error {
|
||||
fmt.Fprintf(w, "// generated by go run gen.go; DO NOT EDIT\n\n")
|
||||
fmt.Fprintf(w, "package publicsuffix\n\nvar rules = [...]string{\n")
|
||||
for _, rule := range rules {
|
||||
fmt.Fprintf(w, "%q,\n", rule)
|
||||
}
|
||||
fmt.Fprintf(w, "}\n\nvar nodeLabels = [...]string{\n")
|
||||
if err := n.walk(w, printNodeLabel); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(w, "}\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
func printReal(w io.Writer, n *node) error {
|
||||
const header = `// generated by go run gen.go; DO NOT EDIT
|
||||
|
||||
package publicsuffix
|
||||
|
||||
const version = %q
|
||||
|
||||
const (
|
||||
nodesBitsChildren = %d
|
||||
nodesBitsICANN = %d
|
||||
nodesBitsTextOffset = %d
|
||||
nodesBitsTextLength = %d
|
||||
|
||||
childrenBitsWildcard = %d
|
||||
childrenBitsNodeType = %d
|
||||
childrenBitsHi = %d
|
||||
childrenBitsLo = %d
|
||||
)
|
||||
|
||||
const (
|
||||
nodeTypeNormal = %d
|
||||
nodeTypeException = %d
|
||||
nodeTypeParentOnly = %d
|
||||
)
|
||||
|
||||
// numTLD is the number of top level domains.
|
||||
const numTLD = %d
|
||||
|
||||
`
|
||||
fmt.Fprintf(w, header, *version,
|
||||
nodesBitsChildren, nodesBitsICANN, nodesBitsTextOffset, nodesBitsTextLength,
|
||||
childrenBitsWildcard, childrenBitsNodeType, childrenBitsHi, childrenBitsLo,
|
||||
nodeTypeNormal, nodeTypeException, nodeTypeParentOnly, len(n.children))
|
||||
|
||||
text := combineText(labelsList)
|
||||
if text == "" {
|
||||
return fmt.Errorf("internal error: makeText returned no text")
|
||||
}
|
||||
for _, label := range labelsList {
|
||||
offset, length := strings.Index(text, label), len(label)
|
||||
if offset < 0 {
|
||||
return fmt.Errorf("internal error: could not find %q in text %q", label, text)
|
||||
}
|
||||
maxTextOffset, maxTextLength = max(maxTextOffset, offset), max(maxTextLength, length)
|
||||
if offset >= 1<<nodesBitsTextOffset {
|
||||
return fmt.Errorf("text offset %d is too large, or nodeBitsTextOffset is too small", offset)
|
||||
}
|
||||
if length >= 1<<nodesBitsTextLength {
|
||||
return fmt.Errorf("text length %d is too large, or nodeBitsTextLength is too small", length)
|
||||
}
|
||||
labelEncoding[label] = uint32(offset)<<nodesBitsTextLength | uint32(length)
|
||||
}
|
||||
fmt.Fprintf(w, "// Text is the combined text of all labels.\nconst text = ")
|
||||
for len(text) > 0 {
|
||||
n, plus := len(text), ""
|
||||
if n > 64 {
|
||||
n, plus = 64, " +"
|
||||
}
|
||||
fmt.Fprintf(w, "%q%s\n", text[:n], plus)
|
||||
text = text[n:]
|
||||
}
|
||||
|
||||
if err := n.walk(w, assignIndexes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, `
|
||||
|
||||
// nodes is the list of nodes. Each node is represented as a uint32, which
|
||||
// encodes the node's children, wildcard bit and node type (as an index into
|
||||
// the children array), ICANN bit and text.
|
||||
//
|
||||
// If the table was generated with the -comments flag, there is a //-comment
|
||||
// after each node's data. In it is the nodes-array indexes of the children,
|
||||
// formatted as (n0x1234-n0x1256), with * denoting the wildcard bit. The
|
||||
// nodeType is printed as + for normal, ! for exception, and o for parent-only
|
||||
// nodes that have children but don't match a domain label in their own right.
|
||||
// An I denotes an ICANN domain.
|
||||
//
|
||||
// The layout within the uint32, from MSB to LSB, is:
|
||||
// [%2d bits] unused
|
||||
// [%2d bits] children index
|
||||
// [%2d bits] ICANN bit
|
||||
// [%2d bits] text index
|
||||
// [%2d bits] text length
|
||||
var nodes = [...]uint32{
|
||||
`,
|
||||
32-nodesBitsChildren-nodesBitsICANN-nodesBitsTextOffset-nodesBitsTextLength,
|
||||
nodesBitsChildren, nodesBitsICANN, nodesBitsTextOffset, nodesBitsTextLength)
|
||||
if err := n.walk(w, printNode); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(w, `}
|
||||
|
||||
// children is the list of nodes' children, the parent's wildcard bit and the
|
||||
// parent's node type. If a node has no children then their children index
|
||||
// will be in the range [0, 6), depending on the wildcard bit and node type.
|
||||
//
|
||||
// The layout within the uint32, from MSB to LSB, is:
|
||||
// [%2d bits] unused
|
||||
// [%2d bits] wildcard bit
|
||||
// [%2d bits] node type
|
||||
// [%2d bits] high nodes index (exclusive) of children
|
||||
// [%2d bits] low nodes index (inclusive) of children
|
||||
var children=[...]uint32{
|
||||
`,
|
||||
32-childrenBitsWildcard-childrenBitsNodeType-childrenBitsHi-childrenBitsLo,
|
||||
childrenBitsWildcard, childrenBitsNodeType, childrenBitsHi, childrenBitsLo)
|
||||
for i, c := range childrenEncoding {
|
||||
s := "---------------"
|
||||
lo := c & (1<<childrenBitsLo - 1)
|
||||
hi := (c >> childrenBitsLo) & (1<<childrenBitsHi - 1)
|
||||
if lo != hi {
|
||||
s = fmt.Sprintf("n0x%04x-n0x%04x", lo, hi)
|
||||
}
|
||||
nodeType := int(c>>(childrenBitsLo+childrenBitsHi)) & (1<<childrenBitsNodeType - 1)
|
||||
wildcard := c>>(childrenBitsLo+childrenBitsHi+childrenBitsNodeType) != 0
|
||||
if *comments {
|
||||
fmt.Fprintf(w, "0x%08x, // c0x%04x (%s)%s %s\n",
|
||||
c, i, s, wildcardStr(wildcard), nodeTypeStr(nodeType))
|
||||
} else {
|
||||
fmt.Fprintf(w, "0x%x,\n", c)
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(w, "}\n\n")
|
||||
fmt.Fprintf(w, "// max children %d (capacity %d)\n", maxChildren, 1<<nodesBitsChildren-1)
|
||||
fmt.Fprintf(w, "// max text offset %d (capacity %d)\n", maxTextOffset, 1<<nodesBitsTextOffset-1)
|
||||
fmt.Fprintf(w, "// max text length %d (capacity %d)\n", maxTextLength, 1<<nodesBitsTextLength-1)
|
||||
fmt.Fprintf(w, "// max hi %d (capacity %d)\n", maxHi, 1<<childrenBitsHi-1)
|
||||
fmt.Fprintf(w, "// max lo %d (capacity %d)\n", maxLo, 1<<childrenBitsLo-1)
|
||||
return nil
|
||||
}
|
||||
|
||||
type node struct {
|
||||
label string
|
||||
nodeType int
|
||||
icann bool
|
||||
wildcard bool
|
||||
// nodesIndex and childrenIndex are the index of this node in the nodes
|
||||
// and the index of its children offset/length in the children arrays.
|
||||
nodesIndex, childrenIndex int
|
||||
// firstChild is the index of this node's first child, or zero if this
|
||||
// node has no children.
|
||||
firstChild int
|
||||
// children are the node's children, in strictly increasing node label order.
|
||||
children []*node
|
||||
}
|
||||
|
||||
func (n *node) walk(w io.Writer, f func(w1 io.Writer, n1 *node) error) error {
|
||||
if err := f(w, n); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, c := range n.children {
|
||||
if err := c.walk(w, f); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// child returns the child of n with the given label. The child is created if
|
||||
// it did not exist beforehand.
|
||||
func (n *node) child(label string) *node {
|
||||
for _, c := range n.children {
|
||||
if c.label == label {
|
||||
return c
|
||||
}
|
||||
}
|
||||
c := &node{
|
||||
label: label,
|
||||
nodeType: nodeTypeParentOnly,
|
||||
icann: true,
|
||||
}
|
||||
n.children = append(n.children, c)
|
||||
sort.Sort(byLabel(n.children))
|
||||
return c
|
||||
}
|
||||
|
||||
type byLabel []*node
|
||||
|
||||
func (b byLabel) Len() int { return len(b) }
|
||||
func (b byLabel) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
||||
func (b byLabel) Less(i, j int) bool { return b[i].label < b[j].label }
|
||||
|
||||
var nextNodesIndex int
|
||||
|
||||
// childrenEncoding are the encoded entries in the generated children array.
|
||||
// All these pre-defined entries have no children.
|
||||
var childrenEncoding = []uint32{
|
||||
0 << (childrenBitsLo + childrenBitsHi), // Without wildcard bit, nodeTypeNormal.
|
||||
1 << (childrenBitsLo + childrenBitsHi), // Without wildcard bit, nodeTypeException.
|
||||
2 << (childrenBitsLo + childrenBitsHi), // Without wildcard bit, nodeTypeParentOnly.
|
||||
4 << (childrenBitsLo + childrenBitsHi), // With wildcard bit, nodeTypeNormal.
|
||||
5 << (childrenBitsLo + childrenBitsHi), // With wildcard bit, nodeTypeException.
|
||||
6 << (childrenBitsLo + childrenBitsHi), // With wildcard bit, nodeTypeParentOnly.
|
||||
}
|
||||
|
||||
var firstCallToAssignIndexes = true
|
||||
|
||||
func assignIndexes(w io.Writer, n *node) error {
|
||||
if len(n.children) != 0 {
|
||||
// Assign nodesIndex.
|
||||
n.firstChild = nextNodesIndex
|
||||
for _, c := range n.children {
|
||||
c.nodesIndex = nextNodesIndex
|
||||
nextNodesIndex++
|
||||
}
|
||||
|
||||
// The root node's children is implicit.
|
||||
if firstCallToAssignIndexes {
|
||||
firstCallToAssignIndexes = false
|
||||
return nil
|
||||
}
|
||||
|
||||
// Assign childrenIndex.
|
||||
maxChildren = max(maxChildren, len(childrenEncoding))
|
||||
if len(childrenEncoding) >= 1<<nodesBitsChildren {
|
||||
return fmt.Errorf("children table size %d is too large, or nodeBitsChildren is too small", len(childrenEncoding))
|
||||
}
|
||||
n.childrenIndex = len(childrenEncoding)
|
||||
lo := uint32(n.firstChild)
|
||||
hi := lo + uint32(len(n.children))
|
||||
maxLo, maxHi = u32max(maxLo, lo), u32max(maxHi, hi)
|
||||
if lo >= 1<<childrenBitsLo {
|
||||
return fmt.Errorf("children lo %d is too large, or childrenBitsLo is too small", lo)
|
||||
}
|
||||
if hi >= 1<<childrenBitsHi {
|
||||
return fmt.Errorf("children hi %d is too large, or childrenBitsHi is too small", hi)
|
||||
}
|
||||
enc := hi<<childrenBitsLo | lo
|
||||
enc |= uint32(n.nodeType) << (childrenBitsLo + childrenBitsHi)
|
||||
if n.wildcard {
|
||||
enc |= 1 << (childrenBitsLo + childrenBitsHi + childrenBitsNodeType)
|
||||
}
|
||||
childrenEncoding = append(childrenEncoding, enc)
|
||||
} else {
|
||||
n.childrenIndex = n.nodeType
|
||||
if n.wildcard {
|
||||
n.childrenIndex += numNodeType
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func printNode(w io.Writer, n *node) error {
|
||||
for _, c := range n.children {
|
||||
s := "---------------"
|
||||
if len(c.children) != 0 {
|
||||
s = fmt.Sprintf("n0x%04x-n0x%04x", c.firstChild, c.firstChild+len(c.children))
|
||||
}
|
||||
encoding := labelEncoding[c.label]
|
||||
if c.icann {
|
||||
encoding |= 1 << (nodesBitsTextLength + nodesBitsTextOffset)
|
||||
}
|
||||
encoding |= uint32(c.childrenIndex) << (nodesBitsTextLength + nodesBitsTextOffset + nodesBitsICANN)
|
||||
if *comments {
|
||||
fmt.Fprintf(w, "0x%08x, // n0x%04x c0x%04x (%s)%s %s %s %s\n",
|
||||
encoding, c.nodesIndex, c.childrenIndex, s, wildcardStr(c.wildcard),
|
||||
nodeTypeStr(c.nodeType), icannStr(c.icann), c.label,
|
||||
)
|
||||
} else {
|
||||
fmt.Fprintf(w, "0x%x,\n", encoding)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func printNodeLabel(w io.Writer, n *node) error {
|
||||
for _, c := range n.children {
|
||||
fmt.Fprintf(w, "%q,\n", c.label)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func icannStr(icann bool) string {
|
||||
if icann {
|
||||
return "I"
|
||||
}
|
||||
return " "
|
||||
}
|
||||
|
||||
func wildcardStr(wildcard bool) string {
|
||||
if wildcard {
|
||||
return "*"
|
||||
}
|
||||
return " "
|
||||
}
|
||||
|
||||
// combineText combines all the strings in labelsList to form one giant string.
|
||||
// Overlapping strings will be merged: "arpa" and "parliament" could yield
|
||||
// "arparliament".
|
||||
func combineText(labelsList []string) string {
|
||||
beforeLength := 0
|
||||
for _, s := range labelsList {
|
||||
beforeLength += len(s)
|
||||
}
|
||||
|
||||
text := crush(removeSubstrings(labelsList))
|
||||
if *v {
|
||||
fmt.Fprintf(os.Stderr, "crushed %d bytes to become %d bytes\n", beforeLength, len(text))
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
type byLength []string
|
||||
|
||||
func (s byLength) Len() int { return len(s) }
|
||||
func (s byLength) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
func (s byLength) Less(i, j int) bool { return len(s[i]) < len(s[j]) }
|
||||
|
||||
// removeSubstrings returns a copy of its input with any strings removed
|
||||
// that are substrings of other provided strings.
|
||||
func removeSubstrings(input []string) []string {
|
||||
// Make a copy of input.
|
||||
ss := append(make([]string, 0, len(input)), input...)
|
||||
sort.Sort(byLength(ss))
|
||||
|
||||
for i, shortString := range ss {
|
||||
// For each string, only consider strings higher than it in sort order, i.e.
|
||||
// of equal length or greater.
|
||||
for _, longString := range ss[i+1:] {
|
||||
if strings.Contains(longString, shortString) {
|
||||
ss[i] = ""
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the empty strings.
|
||||
sort.Strings(ss)
|
||||
for len(ss) > 0 && ss[0] == "" {
|
||||
ss = ss[1:]
|
||||
}
|
||||
return ss
|
||||
}
|
||||
|
||||
// crush combines a list of strings, taking advantage of overlaps. It returns a
|
||||
// single string that contains each input string as a substring.
|
||||
func crush(ss []string) string {
|
||||
maxLabelLen := 0
|
||||
for _, s := range ss {
|
||||
if maxLabelLen < len(s) {
|
||||
maxLabelLen = len(s)
|
||||
}
|
||||
}
|
||||
|
||||
for prefixLen := maxLabelLen; prefixLen > 0; prefixLen-- {
|
||||
prefixes := makePrefixMap(ss, prefixLen)
|
||||
for i, s := range ss {
|
||||
if len(s) <= prefixLen {
|
||||
continue
|
||||
}
|
||||
mergeLabel(ss, i, prefixLen, prefixes)
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(ss, "")
|
||||
}
|
||||
|
||||
// mergeLabel merges the label at ss[i] with the first available matching label
|
||||
// in prefixMap, where the last "prefixLen" characters in ss[i] match the first
|
||||
// "prefixLen" characters in the matching label.
|
||||
// It will merge ss[i] repeatedly until no more matches are available.
|
||||
// All matching labels merged into ss[i] are replaced by "".
|
||||
func mergeLabel(ss []string, i, prefixLen int, prefixes prefixMap) {
|
||||
s := ss[i]
|
||||
suffix := s[len(s)-prefixLen:]
|
||||
for _, j := range prefixes[suffix] {
|
||||
// Empty strings mean "already used." Also avoid merging with self.
|
||||
if ss[j] == "" || i == j {
|
||||
continue
|
||||
}
|
||||
if *v {
|
||||
fmt.Fprintf(os.Stderr, "%d-length overlap at (%4d,%4d): %q and %q share %q\n",
|
||||
prefixLen, i, j, ss[i], ss[j], suffix)
|
||||
}
|
||||
ss[i] += ss[j][prefixLen:]
|
||||
ss[j] = ""
|
||||
// ss[i] has a new suffix, so merge again if possible.
|
||||
// Note: we only have to merge again at the same prefix length. Shorter
|
||||
// prefix lengths will be handled in the next iteration of crush's for loop.
|
||||
// Can there be matches for longer prefix lengths, introduced by the merge?
|
||||
// I believe that any such matches would by necessity have been eliminated
|
||||
// during substring removal or merged at a higher prefix length. For
|
||||
// instance, in crush("abc", "cde", "bcdef"), combining "abc" and "cde"
|
||||
// would yield "abcde", which could be merged with "bcdef." However, in
|
||||
// practice "cde" would already have been elimintated by removeSubstrings.
|
||||
mergeLabel(ss, i, prefixLen, prefixes)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// prefixMap maps from a prefix to a list of strings containing that prefix. The
|
||||
// list of strings is represented as indexes into a slice of strings stored
|
||||
// elsewhere.
|
||||
type prefixMap map[string][]int
|
||||
|
||||
// makePrefixMap constructs a prefixMap from a slice of strings.
|
||||
func makePrefixMap(ss []string, prefixLen int) prefixMap {
|
||||
prefixes := make(prefixMap)
|
||||
for i, s := range ss {
|
||||
// We use < rather than <= because if a label matches on a prefix equal to
|
||||
// its full length, that's actually a substring match handled by
|
||||
// removeSubstrings.
|
||||
if prefixLen < len(s) {
|
||||
prefix := s[:prefixLen]
|
||||
prefixes[prefix] = append(prefixes[prefix], i)
|
||||
}
|
||||
}
|
||||
|
||||
return prefixes
|
||||
}
|
||||
135
vendor/golang.org/x/net/publicsuffix/list.go
generated
vendored
Normal file
135
vendor/golang.org/x/net/publicsuffix/list.go
generated
vendored
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
// Copyright 2012 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.
|
||||
|
||||
//go:generate go run gen.go
|
||||
|
||||
// Package publicsuffix provides a public suffix list based on data from
|
||||
// http://publicsuffix.org/. A public suffix is one under which Internet users
|
||||
// can directly register names.
|
||||
package publicsuffix // import "golang.org/x/net/publicsuffix"
|
||||
|
||||
// TODO: specify case sensitivity and leading/trailing dot behavior for
|
||||
// func PublicSuffix and func EffectiveTLDPlusOne.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http/cookiejar"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// List implements the cookiejar.PublicSuffixList interface by calling the
|
||||
// PublicSuffix function.
|
||||
var List cookiejar.PublicSuffixList = list{}
|
||||
|
||||
type list struct{}
|
||||
|
||||
func (list) PublicSuffix(domain string) string {
|
||||
ps, _ := PublicSuffix(domain)
|
||||
return ps
|
||||
}
|
||||
|
||||
func (list) String() string {
|
||||
return version
|
||||
}
|
||||
|
||||
// PublicSuffix returns the public suffix of the domain using a copy of the
|
||||
// publicsuffix.org database compiled into the library.
|
||||
//
|
||||
// icann is whether the public suffix is managed by the Internet Corporation
|
||||
// for Assigned Names and Numbers. If not, the public suffix is privately
|
||||
// managed. For example, foo.org and foo.co.uk are ICANN domains,
|
||||
// foo.dyndns.org and foo.blogspot.co.uk are private domains.
|
||||
//
|
||||
// Use cases for distinguishing ICANN domains like foo.com from private
|
||||
// domains like foo.appspot.com can be found at
|
||||
// https://wiki.mozilla.org/Public_Suffix_List/Use_Cases
|
||||
func PublicSuffix(domain string) (publicSuffix string, icann bool) {
|
||||
lo, hi := uint32(0), uint32(numTLD)
|
||||
s, suffix, wildcard := domain, len(domain), false
|
||||
loop:
|
||||
for {
|
||||
dot := strings.LastIndex(s, ".")
|
||||
if wildcard {
|
||||
suffix = 1 + dot
|
||||
}
|
||||
if lo == hi {
|
||||
break
|
||||
}
|
||||
f := find(s[1+dot:], lo, hi)
|
||||
if f == notFound {
|
||||
break
|
||||
}
|
||||
|
||||
u := nodes[f] >> (nodesBitsTextOffset + nodesBitsTextLength)
|
||||
icann = u&(1<<nodesBitsICANN-1) != 0
|
||||
u >>= nodesBitsICANN
|
||||
u = children[u&(1<<nodesBitsChildren-1)]
|
||||
lo = u & (1<<childrenBitsLo - 1)
|
||||
u >>= childrenBitsLo
|
||||
hi = u & (1<<childrenBitsHi - 1)
|
||||
u >>= childrenBitsHi
|
||||
switch u & (1<<childrenBitsNodeType - 1) {
|
||||
case nodeTypeNormal:
|
||||
suffix = 1 + dot
|
||||
case nodeTypeException:
|
||||
suffix = 1 + len(s)
|
||||
break loop
|
||||
}
|
||||
u >>= childrenBitsNodeType
|
||||
wildcard = u&(1<<childrenBitsWildcard-1) != 0
|
||||
|
||||
if dot == -1 {
|
||||
break
|
||||
}
|
||||
s = s[:dot]
|
||||
}
|
||||
if suffix == len(domain) {
|
||||
// If no rules match, the prevailing rule is "*".
|
||||
return domain[1+strings.LastIndex(domain, "."):], icann
|
||||
}
|
||||
return domain[suffix:], icann
|
||||
}
|
||||
|
||||
const notFound uint32 = 1<<32 - 1
|
||||
|
||||
// find returns the index of the node in the range [lo, hi) whose label equals
|
||||
// label, or notFound if there is no such node. The range is assumed to be in
|
||||
// strictly increasing node label order.
|
||||
func find(label string, lo, hi uint32) uint32 {
|
||||
for lo < hi {
|
||||
mid := lo + (hi-lo)/2
|
||||
s := nodeLabel(mid)
|
||||
if s < label {
|
||||
lo = mid + 1
|
||||
} else if s == label {
|
||||
return mid
|
||||
} else {
|
||||
hi = mid
|
||||
}
|
||||
}
|
||||
return notFound
|
||||
}
|
||||
|
||||
// nodeLabel returns the label for the i'th node.
|
||||
func nodeLabel(i uint32) string {
|
||||
x := nodes[i]
|
||||
length := x & (1<<nodesBitsTextLength - 1)
|
||||
x >>= nodesBitsTextLength
|
||||
offset := x & (1<<nodesBitsTextOffset - 1)
|
||||
return text[offset : offset+length]
|
||||
}
|
||||
|
||||
// EffectiveTLDPlusOne returns the effective top level domain plus one more
|
||||
// label. For example, the eTLD+1 for "foo.bar.golang.org" is "golang.org".
|
||||
func EffectiveTLDPlusOne(domain string) (string, error) {
|
||||
suffix, _ := PublicSuffix(domain)
|
||||
if len(domain) <= len(suffix) {
|
||||
return "", fmt.Errorf("publicsuffix: cannot derive eTLD+1 for domain %q", domain)
|
||||
}
|
||||
i := len(domain) - len(suffix) - 1
|
||||
if domain[i] != '.' {
|
||||
return "", fmt.Errorf("publicsuffix: invalid public suffix %q for domain %q", suffix, domain)
|
||||
}
|
||||
return domain[1+strings.LastIndex(domain[:i], "."):], nil
|
||||
}
|
||||
416
vendor/golang.org/x/net/publicsuffix/list_test.go
generated
vendored
Normal file
416
vendor/golang.org/x/net/publicsuffix/list_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,416 @@
|
|||
// Copyright 2012 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 publicsuffix
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNodeLabel(t *testing.T) {
|
||||
for i, want := range nodeLabels {
|
||||
got := nodeLabel(uint32(i))
|
||||
if got != want {
|
||||
t.Errorf("%d: got %q, want %q", i, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFind(t *testing.T) {
|
||||
testCases := []string{
|
||||
"",
|
||||
"a",
|
||||
"a0",
|
||||
"aaaa",
|
||||
"ao",
|
||||
"ap",
|
||||
"ar",
|
||||
"aro",
|
||||
"arp",
|
||||
"arpa",
|
||||
"arpaa",
|
||||
"arpb",
|
||||
"az",
|
||||
"b",
|
||||
"b0",
|
||||
"ba",
|
||||
"z",
|
||||
"zu",
|
||||
"zv",
|
||||
"zw",
|
||||
"zx",
|
||||
"zy",
|
||||
"zz",
|
||||
"zzzz",
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
got := find(tc, 0, numTLD)
|
||||
want := notFound
|
||||
for i := uint32(0); i < numTLD; i++ {
|
||||
if tc == nodeLabel(i) {
|
||||
want = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if got != want {
|
||||
t.Errorf("%q: got %d, want %d", tc, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestICANN(t *testing.T) {
|
||||
testCases := map[string]bool{
|
||||
"foo.org": true,
|
||||
"foo.co.uk": true,
|
||||
"foo.dyndns.org": false,
|
||||
"foo.go.dyndns.org": false,
|
||||
"foo.blogspot.co.uk": false,
|
||||
"foo.intranet": false,
|
||||
}
|
||||
for domain, want := range testCases {
|
||||
_, got := PublicSuffix(domain)
|
||||
if got != want {
|
||||
t.Errorf("%q: got %v, want %v", domain, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var publicSuffixTestCases = []struct {
|
||||
domain, want string
|
||||
}{
|
||||
// Empty string.
|
||||
{"", ""},
|
||||
|
||||
// The .ao rules are:
|
||||
// ao
|
||||
// ed.ao
|
||||
// gv.ao
|
||||
// og.ao
|
||||
// co.ao
|
||||
// pb.ao
|
||||
// it.ao
|
||||
{"ao", "ao"},
|
||||
{"www.ao", "ao"},
|
||||
{"pb.ao", "pb.ao"},
|
||||
{"www.pb.ao", "pb.ao"},
|
||||
{"www.xxx.yyy.zzz.pb.ao", "pb.ao"},
|
||||
|
||||
// The .ar rules are:
|
||||
// ar
|
||||
// com.ar
|
||||
// edu.ar
|
||||
// gob.ar
|
||||
// gov.ar
|
||||
// int.ar
|
||||
// mil.ar
|
||||
// net.ar
|
||||
// org.ar
|
||||
// tur.ar
|
||||
// blogspot.com.ar
|
||||
{"ar", "ar"},
|
||||
{"www.ar", "ar"},
|
||||
{"nic.ar", "ar"},
|
||||
{"www.nic.ar", "ar"},
|
||||
{"com.ar", "com.ar"},
|
||||
{"www.com.ar", "com.ar"},
|
||||
{"blogspot.com.ar", "blogspot.com.ar"},
|
||||
{"www.blogspot.com.ar", "blogspot.com.ar"},
|
||||
{"www.xxx.yyy.zzz.blogspot.com.ar", "blogspot.com.ar"},
|
||||
{"logspot.com.ar", "com.ar"},
|
||||
{"zlogspot.com.ar", "com.ar"},
|
||||
{"zblogspot.com.ar", "com.ar"},
|
||||
|
||||
// The .arpa rules are:
|
||||
// arpa
|
||||
// e164.arpa
|
||||
// in-addr.arpa
|
||||
// ip6.arpa
|
||||
// iris.arpa
|
||||
// uri.arpa
|
||||
// urn.arpa
|
||||
{"arpa", "arpa"},
|
||||
{"www.arpa", "arpa"},
|
||||
{"urn.arpa", "urn.arpa"},
|
||||
{"www.urn.arpa", "urn.arpa"},
|
||||
{"www.xxx.yyy.zzz.urn.arpa", "urn.arpa"},
|
||||
|
||||
// The relevant {kobe,kyoto}.jp rules are:
|
||||
// jp
|
||||
// *.kobe.jp
|
||||
// !city.kobe.jp
|
||||
// kyoto.jp
|
||||
// ide.kyoto.jp
|
||||
{"jp", "jp"},
|
||||
{"kobe.jp", "jp"},
|
||||
{"c.kobe.jp", "c.kobe.jp"},
|
||||
{"b.c.kobe.jp", "c.kobe.jp"},
|
||||
{"a.b.c.kobe.jp", "c.kobe.jp"},
|
||||
{"city.kobe.jp", "kobe.jp"},
|
||||
{"www.city.kobe.jp", "kobe.jp"},
|
||||
{"kyoto.jp", "kyoto.jp"},
|
||||
{"test.kyoto.jp", "kyoto.jp"},
|
||||
{"ide.kyoto.jp", "ide.kyoto.jp"},
|
||||
{"b.ide.kyoto.jp", "ide.kyoto.jp"},
|
||||
{"a.b.ide.kyoto.jp", "ide.kyoto.jp"},
|
||||
|
||||
// The .tw rules are:
|
||||
// tw
|
||||
// edu.tw
|
||||
// gov.tw
|
||||
// mil.tw
|
||||
// com.tw
|
||||
// net.tw
|
||||
// org.tw
|
||||
// idv.tw
|
||||
// game.tw
|
||||
// ebiz.tw
|
||||
// club.tw
|
||||
// 網路.tw (xn--zf0ao64a.tw)
|
||||
// 組織.tw (xn--uc0atv.tw)
|
||||
// 商業.tw (xn--czrw28b.tw)
|
||||
// blogspot.tw
|
||||
{"tw", "tw"},
|
||||
{"aaa.tw", "tw"},
|
||||
{"www.aaa.tw", "tw"},
|
||||
{"xn--czrw28b.aaa.tw", "tw"},
|
||||
{"edu.tw", "edu.tw"},
|
||||
{"www.edu.tw", "edu.tw"},
|
||||
{"xn--czrw28b.edu.tw", "edu.tw"},
|
||||
{"xn--czrw28b.tw", "xn--czrw28b.tw"},
|
||||
{"www.xn--czrw28b.tw", "xn--czrw28b.tw"},
|
||||
{"xn--uc0atv.xn--czrw28b.tw", "xn--czrw28b.tw"},
|
||||
{"xn--kpry57d.tw", "tw"},
|
||||
|
||||
// The .uk rules are:
|
||||
// uk
|
||||
// ac.uk
|
||||
// co.uk
|
||||
// gov.uk
|
||||
// ltd.uk
|
||||
// me.uk
|
||||
// net.uk
|
||||
// nhs.uk
|
||||
// org.uk
|
||||
// plc.uk
|
||||
// police.uk
|
||||
// *.sch.uk
|
||||
// blogspot.co.uk
|
||||
{"uk", "uk"},
|
||||
{"aaa.uk", "uk"},
|
||||
{"www.aaa.uk", "uk"},
|
||||
{"mod.uk", "uk"},
|
||||
{"www.mod.uk", "uk"},
|
||||
{"sch.uk", "uk"},
|
||||
{"mod.sch.uk", "mod.sch.uk"},
|
||||
{"www.sch.uk", "www.sch.uk"},
|
||||
{"blogspot.co.uk", "blogspot.co.uk"},
|
||||
{"blogspot.nic.uk", "uk"},
|
||||
{"blogspot.sch.uk", "blogspot.sch.uk"},
|
||||
|
||||
// The .рф rules are
|
||||
// рф (xn--p1ai)
|
||||
{"xn--p1ai", "xn--p1ai"},
|
||||
{"aaa.xn--p1ai", "xn--p1ai"},
|
||||
{"www.xxx.yyy.xn--p1ai", "xn--p1ai"},
|
||||
|
||||
// The .bd rules are:
|
||||
// *.bd
|
||||
{"bd", "bd"},
|
||||
{"www.bd", "www.bd"},
|
||||
{"zzz.bd", "zzz.bd"},
|
||||
{"www.zzz.bd", "zzz.bd"},
|
||||
{"www.xxx.yyy.zzz.bd", "zzz.bd"},
|
||||
|
||||
// There are no .nosuchtld rules.
|
||||
{"nosuchtld", "nosuchtld"},
|
||||
{"foo.nosuchtld", "nosuchtld"},
|
||||
{"bar.foo.nosuchtld", "nosuchtld"},
|
||||
}
|
||||
|
||||
func BenchmarkPublicSuffix(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, tc := range publicSuffixTestCases {
|
||||
List.PublicSuffix(tc.domain)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPublicSuffix(t *testing.T) {
|
||||
for _, tc := range publicSuffixTestCases {
|
||||
got := List.PublicSuffix(tc.domain)
|
||||
if got != tc.want {
|
||||
t.Errorf("%q: got %q, want %q", tc.domain, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSlowPublicSuffix(t *testing.T) {
|
||||
for _, tc := range publicSuffixTestCases {
|
||||
got := slowPublicSuffix(tc.domain)
|
||||
if got != tc.want {
|
||||
t.Errorf("%q: got %q, want %q", tc.domain, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// slowPublicSuffix implements the canonical (but O(number of rules)) public
|
||||
// suffix algorithm described at http://publicsuffix.org/list/.
|
||||
//
|
||||
// 1. Match domain against all rules and take note of the matching ones.
|
||||
// 2. If no rules match, the prevailing rule is "*".
|
||||
// 3. If more than one rule matches, the prevailing rule is the one which is an exception rule.
|
||||
// 4. If there is no matching exception rule, the prevailing rule is the one with the most labels.
|
||||
// 5. If the prevailing rule is a exception rule, modify it by removing the leftmost label.
|
||||
// 6. The public suffix is the set of labels from the domain which directly match the labels of the prevailing rule (joined by dots).
|
||||
// 7. The registered or registrable domain is the public suffix plus one additional label.
|
||||
//
|
||||
// This function returns the public suffix, not the registrable domain, and so
|
||||
// it stops after step 6.
|
||||
func slowPublicSuffix(domain string) string {
|
||||
match := func(rulePart, domainPart string) bool {
|
||||
switch rulePart[0] {
|
||||
case '*':
|
||||
return true
|
||||
case '!':
|
||||
return rulePart[1:] == domainPart
|
||||
}
|
||||
return rulePart == domainPart
|
||||
}
|
||||
|
||||
domainParts := strings.Split(domain, ".")
|
||||
var matchingRules [][]string
|
||||
|
||||
loop:
|
||||
for _, rule := range rules {
|
||||
ruleParts := strings.Split(rule, ".")
|
||||
if len(domainParts) < len(ruleParts) {
|
||||
continue
|
||||
}
|
||||
for i := range ruleParts {
|
||||
rulePart := ruleParts[len(ruleParts)-1-i]
|
||||
domainPart := domainParts[len(domainParts)-1-i]
|
||||
if !match(rulePart, domainPart) {
|
||||
continue loop
|
||||
}
|
||||
}
|
||||
matchingRules = append(matchingRules, ruleParts)
|
||||
}
|
||||
if len(matchingRules) == 0 {
|
||||
matchingRules = append(matchingRules, []string{"*"})
|
||||
} else {
|
||||
sort.Sort(byPriority(matchingRules))
|
||||
}
|
||||
prevailing := matchingRules[0]
|
||||
if prevailing[0][0] == '!' {
|
||||
prevailing = prevailing[1:]
|
||||
}
|
||||
if prevailing[0][0] == '*' {
|
||||
replaced := domainParts[len(domainParts)-len(prevailing)]
|
||||
prevailing = append([]string{replaced}, prevailing[1:]...)
|
||||
}
|
||||
return strings.Join(prevailing, ".")
|
||||
}
|
||||
|
||||
type byPriority [][]string
|
||||
|
||||
func (b byPriority) Len() int { return len(b) }
|
||||
func (b byPriority) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
||||
func (b byPriority) Less(i, j int) bool {
|
||||
if b[i][0][0] == '!' {
|
||||
return true
|
||||
}
|
||||
if b[j][0][0] == '!' {
|
||||
return false
|
||||
}
|
||||
return len(b[i]) > len(b[j])
|
||||
}
|
||||
|
||||
// eTLDPlusOneTestCases come from
|
||||
// https://github.com/publicsuffix/list/blob/master/tests/test_psl.txt
|
||||
var eTLDPlusOneTestCases = []struct {
|
||||
domain, want string
|
||||
}{
|
||||
// Empty input.
|
||||
{"", ""},
|
||||
// Unlisted TLD.
|
||||
{"example", ""},
|
||||
{"example.example", "example.example"},
|
||||
{"b.example.example", "example.example"},
|
||||
{"a.b.example.example", "example.example"},
|
||||
// TLD with only 1 rule.
|
||||
{"biz", ""},
|
||||
{"domain.biz", "domain.biz"},
|
||||
{"b.domain.biz", "domain.biz"},
|
||||
{"a.b.domain.biz", "domain.biz"},
|
||||
// TLD with some 2-level rules.
|
||||
{"com", ""},
|
||||
{"example.com", "example.com"},
|
||||
{"b.example.com", "example.com"},
|
||||
{"a.b.example.com", "example.com"},
|
||||
{"uk.com", ""},
|
||||
{"example.uk.com", "example.uk.com"},
|
||||
{"b.example.uk.com", "example.uk.com"},
|
||||
{"a.b.example.uk.com", "example.uk.com"},
|
||||
{"test.ac", "test.ac"},
|
||||
// TLD with only 1 (wildcard) rule.
|
||||
{"mm", ""},
|
||||
{"c.mm", ""},
|
||||
{"b.c.mm", "b.c.mm"},
|
||||
{"a.b.c.mm", "b.c.mm"},
|
||||
// More complex TLD.
|
||||
{"jp", ""},
|
||||
{"test.jp", "test.jp"},
|
||||
{"www.test.jp", "test.jp"},
|
||||
{"ac.jp", ""},
|
||||
{"test.ac.jp", "test.ac.jp"},
|
||||
{"www.test.ac.jp", "test.ac.jp"},
|
||||
{"kyoto.jp", ""},
|
||||
{"test.kyoto.jp", "test.kyoto.jp"},
|
||||
{"ide.kyoto.jp", ""},
|
||||
{"b.ide.kyoto.jp", "b.ide.kyoto.jp"},
|
||||
{"a.b.ide.kyoto.jp", "b.ide.kyoto.jp"},
|
||||
{"c.kobe.jp", ""},
|
||||
{"b.c.kobe.jp", "b.c.kobe.jp"},
|
||||
{"a.b.c.kobe.jp", "b.c.kobe.jp"},
|
||||
{"city.kobe.jp", "city.kobe.jp"},
|
||||
{"www.city.kobe.jp", "city.kobe.jp"},
|
||||
// TLD with a wildcard rule and exceptions.
|
||||
{"ck", ""},
|
||||
{"test.ck", ""},
|
||||
{"b.test.ck", "b.test.ck"},
|
||||
{"a.b.test.ck", "b.test.ck"},
|
||||
{"www.ck", "www.ck"},
|
||||
{"www.www.ck", "www.ck"},
|
||||
// US K12.
|
||||
{"us", ""},
|
||||
{"test.us", "test.us"},
|
||||
{"www.test.us", "test.us"},
|
||||
{"ak.us", ""},
|
||||
{"test.ak.us", "test.ak.us"},
|
||||
{"www.test.ak.us", "test.ak.us"},
|
||||
{"k12.ak.us", ""},
|
||||
{"test.k12.ak.us", "test.k12.ak.us"},
|
||||
{"www.test.k12.ak.us", "test.k12.ak.us"},
|
||||
// Punycoded IDN labels
|
||||
{"xn--85x722f.com.cn", "xn--85x722f.com.cn"},
|
||||
{"xn--85x722f.xn--55qx5d.cn", "xn--85x722f.xn--55qx5d.cn"},
|
||||
{"www.xn--85x722f.xn--55qx5d.cn", "xn--85x722f.xn--55qx5d.cn"},
|
||||
{"shishi.xn--55qx5d.cn", "shishi.xn--55qx5d.cn"},
|
||||
{"xn--55qx5d.cn", ""},
|
||||
{"xn--85x722f.xn--fiqs8s", "xn--85x722f.xn--fiqs8s"},
|
||||
{"www.xn--85x722f.xn--fiqs8s", "xn--85x722f.xn--fiqs8s"},
|
||||
{"shishi.xn--fiqs8s", "shishi.xn--fiqs8s"},
|
||||
{"xn--fiqs8s", ""},
|
||||
}
|
||||
|
||||
func TestEffectiveTLDPlusOne(t *testing.T) {
|
||||
for _, tc := range eTLDPlusOneTestCases {
|
||||
got, _ := EffectiveTLDPlusOne(tc.domain)
|
||||
if got != tc.want {
|
||||
t.Errorf("%q: got %q, want %q", tc.domain, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
9419
vendor/golang.org/x/net/publicsuffix/table.go
generated
vendored
Normal file
9419
vendor/golang.org/x/net/publicsuffix/table.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
16756
vendor/golang.org/x/net/publicsuffix/table_test.go
generated
vendored
Normal file
16756
vendor/golang.org/x/net/publicsuffix/table_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
13
vendor/golang.org/x/oauth2/.travis.yml
generated
vendored
Normal file
13
vendor/golang.org/x/oauth2/.travis.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- tip
|
||||
|
||||
install:
|
||||
- export GOPATH="$HOME/gopath"
|
||||
- mkdir -p "$GOPATH/src/golang.org/x"
|
||||
- mv "$TRAVIS_BUILD_DIR" "$GOPATH/src/golang.org/x/oauth2"
|
||||
- go get -v -t -d golang.org/x/oauth2/...
|
||||
|
||||
script:
|
||||
- go test -v golang.org/x/oauth2/...
|
||||
3
vendor/golang.org/x/oauth2/AUTHORS
generated
vendored
Normal file
3
vendor/golang.org/x/oauth2/AUTHORS
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# This source code refers to The Go Authors for copyright purposes.
|
||||
# The master list of authors is in the main Go distribution,
|
||||
# visible at http://tip.golang.org/AUTHORS.
|
||||
31
vendor/golang.org/x/oauth2/CONTRIBUTING.md
generated
vendored
Normal file
31
vendor/golang.org/x/oauth2/CONTRIBUTING.md
generated
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# Contributing to Go
|
||||
|
||||
Go is an open source project.
|
||||
|
||||
It is the work of hundreds of contributors. We appreciate your help!
|
||||
|
||||
|
||||
## Filing issues
|
||||
|
||||
When [filing an issue](https://github.com/golang/oauth2/issues), make sure to answer these five questions:
|
||||
|
||||
1. What version of Go are you using (`go version`)?
|
||||
2. What operating system and processor architecture are you using?
|
||||
3. What did you do?
|
||||
4. What did you expect to see?
|
||||
5. What did you see instead?
|
||||
|
||||
General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker.
|
||||
The gophers there will answer or ask you to file an issue if you've tripped over a bug.
|
||||
|
||||
## Contributing code
|
||||
|
||||
Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
|
||||
before sending patches.
|
||||
|
||||
**We do not accept GitHub pull requests**
|
||||
(we use [Gerrit](https://code.google.com/p/gerrit/) instead for code review).
|
||||
|
||||
Unless otherwise noted, the Go source files are distributed under
|
||||
the BSD-style license found in the LICENSE file.
|
||||
|
||||
3
vendor/golang.org/x/oauth2/CONTRIBUTORS
generated
vendored
Normal file
3
vendor/golang.org/x/oauth2/CONTRIBUTORS
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# This source code was written by the Go contributors.
|
||||
# The master list of contributors is in the main Go distribution,
|
||||
# visible at http://tip.golang.org/CONTRIBUTORS.
|
||||
27
vendor/golang.org/x/oauth2/LICENSE
generated
vendored
Normal file
27
vendor/golang.org/x/oauth2/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
77
vendor/golang.org/x/oauth2/README.md
generated
vendored
Normal file
77
vendor/golang.org/x/oauth2/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
# OAuth2 for Go
|
||||
|
||||
[](https://travis-ci.org/golang/oauth2)
|
||||
[](https://godoc.org/golang.org/x/oauth2)
|
||||
|
||||
oauth2 package contains a client implementation for OAuth 2.0 spec.
|
||||
|
||||
## Installation
|
||||
|
||||
~~~~
|
||||
go get golang.org/x/oauth2
|
||||
~~~~
|
||||
|
||||
Or you can manually git clone the repository to
|
||||
`$(go env GOPATH)/src/golang.org/x/oauth2`.
|
||||
|
||||
See godoc for further documentation and examples.
|
||||
|
||||
* [godoc.org/golang.org/x/oauth2](http://godoc.org/golang.org/x/oauth2)
|
||||
* [godoc.org/golang.org/x/oauth2/google](http://godoc.org/golang.org/x/oauth2/google)
|
||||
|
||||
|
||||
## App Engine
|
||||
|
||||
In change 96e89be (March 2015), we removed the `oauth2.Context2` type in favor
|
||||
of the [`context.Context`](https://golang.org/x/net/context#Context) type from
|
||||
the `golang.org/x/net/context` package
|
||||
|
||||
This means it's no longer possible to use the "Classic App Engine"
|
||||
`appengine.Context` type with the `oauth2` package. (You're using
|
||||
Classic App Engine if you import the package `"appengine"`.)
|
||||
|
||||
To work around this, you may use the new `"google.golang.org/appengine"`
|
||||
package. This package has almost the same API as the `"appengine"` package,
|
||||
but it can be fetched with `go get` and used on "Managed VMs" and well as
|
||||
Classic App Engine.
|
||||
|
||||
See the [new `appengine` package's readme](https://github.com/golang/appengine#updating-a-go-app-engine-app)
|
||||
for information on updating your app.
|
||||
|
||||
If you don't want to update your entire app to use the new App Engine packages,
|
||||
you may use both sets of packages in parallel, using only the new packages
|
||||
with the `oauth2` package.
|
||||
|
||||
```go
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
newappengine "google.golang.org/appengine"
|
||||
newurlfetch "google.golang.org/appengine/urlfetch"
|
||||
|
||||
"appengine"
|
||||
)
|
||||
|
||||
func handler(w http.ResponseWriter, r *http.Request) {
|
||||
var c appengine.Context = appengine.NewContext(r)
|
||||
c.Infof("Logging a message with the old package")
|
||||
|
||||
var ctx context.Context = newappengine.NewContext(r)
|
||||
client := &http.Client{
|
||||
Transport: &oauth2.Transport{
|
||||
Source: google.AppEngineTokenSource(ctx, "scope"),
|
||||
Base: &newurlfetch.Transport{Context: ctx},
|
||||
},
|
||||
}
|
||||
client.Get("...")
|
||||
}
|
||||
```
|
||||
|
||||
## Report Issues / Send Patches
|
||||
|
||||
This repository uses Gerrit for code changes. To learn how to submit changes to
|
||||
this repository, see https://golang.org/doc/contribute.html.
|
||||
|
||||
The main issue tracker for the oauth2 repository is located at
|
||||
https://github.com/golang/oauth2/issues.
|
||||
25
vendor/golang.org/x/oauth2/client_appengine.go
generated
vendored
Normal file
25
vendor/golang.org/x/oauth2/client_appengine.go
generated
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// 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 appengine
|
||||
|
||||
// App Engine hooks.
|
||||
|
||||
package oauth2
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/oauth2/internal"
|
||||
"google.golang.org/appengine/urlfetch"
|
||||
)
|
||||
|
||||
func init() {
|
||||
internal.RegisterContextClientFunc(contextClientAppEngine)
|
||||
}
|
||||
|
||||
func contextClientAppEngine(ctx context.Context) (*http.Client, error) {
|
||||
return urlfetch.Client(ctx), nil
|
||||
}
|
||||
89
vendor/golang.org/x/oauth2/example_test.go
generated
vendored
Normal file
89
vendor/golang.org/x/oauth2/example_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package oauth2_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
func ExampleConfig() {
|
||||
ctx := context.Background()
|
||||
conf := &oauth2.Config{
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
ClientSecret: "YOUR_CLIENT_SECRET",
|
||||
Scopes: []string{"SCOPE1", "SCOPE2"},
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: "https://provider.com/o/oauth2/auth",
|
||||
TokenURL: "https://provider.com/o/oauth2/token",
|
||||
},
|
||||
}
|
||||
|
||||
// Redirect user to consent page to ask for permission
|
||||
// for the scopes specified above.
|
||||
url := conf.AuthCodeURL("state", oauth2.AccessTypeOffline)
|
||||
fmt.Printf("Visit the URL for the auth dialog: %v", url)
|
||||
|
||||
// Use the authorization code that is pushed to the redirect
|
||||
// URL. Exchange will do the handshake to retrieve the
|
||||
// initial access token. The HTTP Client returned by
|
||||
// conf.Client will refresh the token as necessary.
|
||||
var code string
|
||||
if _, err := fmt.Scan(&code); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
tok, err := conf.Exchange(ctx, code)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
client := conf.Client(ctx, tok)
|
||||
client.Get("...")
|
||||
}
|
||||
|
||||
func ExampleConfig_customHTTP() {
|
||||
ctx := context.Background()
|
||||
|
||||
conf := &oauth2.Config{
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
ClientSecret: "YOUR_CLIENT_SECRET",
|
||||
Scopes: []string{"SCOPE1", "SCOPE2"},
|
||||
Endpoint: oauth2.Endpoint{
|
||||
TokenURL: "https://provider.com/o/oauth2/token",
|
||||
AuthURL: "https://provider.com/o/oauth2/auth",
|
||||
},
|
||||
}
|
||||
|
||||
// Redirect user to consent page to ask for permission
|
||||
// for the scopes specified above.
|
||||
url := conf.AuthCodeURL("state", oauth2.AccessTypeOffline)
|
||||
fmt.Printf("Visit the URL for the auth dialog: %v", url)
|
||||
|
||||
// Use the authorization code that is pushed to the redirect
|
||||
// URL. Exchange will do the handshake to retrieve the
|
||||
// initial access token. The HTTP Client returned by
|
||||
// conf.Client will refresh the token as necessary.
|
||||
var code string
|
||||
if _, err := fmt.Scan(&code); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Use the custom HTTP client when requesting a token.
|
||||
httpClient := &http.Client{Timeout: 2 * time.Second}
|
||||
ctx = context.WithValue(ctx, oauth2.HTTPClient, httpClient)
|
||||
|
||||
tok, err := conf.Exchange(ctx, code)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
client := conf.Client(ctx, tok)
|
||||
_ = client
|
||||
}
|
||||
89
vendor/golang.org/x/oauth2/google/appengine.go
generated
vendored
Normal file
89
vendor/golang.org/x/oauth2/google/appengine.go
generated
vendored
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package google
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
// appengineFlex is set at init time by appengineflex_hook.go. If true, we are on App Engine Flex.
|
||||
var appengineFlex bool
|
||||
|
||||
// Set at init time by appengine_hook.go. If nil, we're not on App Engine.
|
||||
var appengineTokenFunc func(c context.Context, scopes ...string) (token string, expiry time.Time, err error)
|
||||
|
||||
// Set at init time by appengine_hook.go. If nil, we're not on App Engine.
|
||||
var appengineAppIDFunc func(c context.Context) string
|
||||
|
||||
// AppEngineTokenSource returns a token source that fetches tokens
|
||||
// issued to the current App Engine application's service account.
|
||||
// If you are implementing a 3-legged OAuth 2.0 flow on App Engine
|
||||
// that involves user accounts, see oauth2.Config instead.
|
||||
//
|
||||
// The provided context must have come from appengine.NewContext.
|
||||
func AppEngineTokenSource(ctx context.Context, scope ...string) oauth2.TokenSource {
|
||||
if appengineTokenFunc == nil {
|
||||
panic("google: AppEngineTokenSource can only be used on App Engine.")
|
||||
}
|
||||
scopes := append([]string{}, scope...)
|
||||
sort.Strings(scopes)
|
||||
return &appEngineTokenSource{
|
||||
ctx: ctx,
|
||||
scopes: scopes,
|
||||
key: strings.Join(scopes, " "),
|
||||
}
|
||||
}
|
||||
|
||||
// aeTokens helps the fetched tokens to be reused until their expiration.
|
||||
var (
|
||||
aeTokensMu sync.Mutex
|
||||
aeTokens = make(map[string]*tokenLock) // key is space-separated scopes
|
||||
)
|
||||
|
||||
type tokenLock struct {
|
||||
mu sync.Mutex // guards t; held while fetching or updating t
|
||||
t *oauth2.Token
|
||||
}
|
||||
|
||||
type appEngineTokenSource struct {
|
||||
ctx context.Context
|
||||
scopes []string
|
||||
key string // to aeTokens map; space-separated scopes
|
||||
}
|
||||
|
||||
func (ts *appEngineTokenSource) Token() (*oauth2.Token, error) {
|
||||
if appengineTokenFunc == nil {
|
||||
panic("google: AppEngineTokenSource can only be used on App Engine.")
|
||||
}
|
||||
|
||||
aeTokensMu.Lock()
|
||||
tok, ok := aeTokens[ts.key]
|
||||
if !ok {
|
||||
tok = &tokenLock{}
|
||||
aeTokens[ts.key] = tok
|
||||
}
|
||||
aeTokensMu.Unlock()
|
||||
|
||||
tok.mu.Lock()
|
||||
defer tok.mu.Unlock()
|
||||
if tok.t.Valid() {
|
||||
return tok.t, nil
|
||||
}
|
||||
access, exp, err := appengineTokenFunc(ts.ctx, ts.scopes...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tok.t = &oauth2.Token{
|
||||
AccessToken: access,
|
||||
Expiry: exp,
|
||||
}
|
||||
return tok.t, nil
|
||||
}
|
||||
14
vendor/golang.org/x/oauth2/google/appengine_hook.go
generated
vendored
Normal file
14
vendor/golang.org/x/oauth2/google/appengine_hook.go
generated
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
// 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 appengine appenginevm
|
||||
|
||||
package google
|
||||
|
||||
import "google.golang.org/appengine"
|
||||
|
||||
func init() {
|
||||
appengineTokenFunc = appengine.AccessToken
|
||||
appengineAppIDFunc = appengine.AppID
|
||||
}
|
||||
11
vendor/golang.org/x/oauth2/google/appengineflex_hook.go
generated
vendored
Normal file
11
vendor/golang.org/x/oauth2/google/appengineflex_hook.go
generated
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build appenginevm
|
||||
|
||||
package google
|
||||
|
||||
func init() {
|
||||
appengineFlex = true // Flex doesn't support appengine.AccessToken; depend on metadata server.
|
||||
}
|
||||
137
vendor/golang.org/x/oauth2/google/default.go
generated
vendored
Normal file
137
vendor/golang.org/x/oauth2/google/default.go
generated
vendored
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
// 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 google
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"cloud.google.com/go/compute/metadata"
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
// DefaultCredentials holds "Application Default Credentials".
|
||||
// For more details, see:
|
||||
// https://developers.google.com/accounts/docs/application-default-credentials
|
||||
type DefaultCredentials struct {
|
||||
ProjectID string // may be empty
|
||||
TokenSource oauth2.TokenSource
|
||||
|
||||
// JSON contains the raw bytes from a JSON credentials file.
|
||||
// This field may be nil if authentication is provided by the
|
||||
// environment and not with a credentials file, e.g. when code is
|
||||
// running on Google Cloud Platform.
|
||||
JSON []byte
|
||||
}
|
||||
|
||||
// DefaultClient returns an HTTP Client that uses the
|
||||
// DefaultTokenSource to obtain authentication credentials.
|
||||
func DefaultClient(ctx context.Context, scope ...string) (*http.Client, error) {
|
||||
ts, err := DefaultTokenSource(ctx, scope...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return oauth2.NewClient(ctx, ts), nil
|
||||
}
|
||||
|
||||
// DefaultTokenSource returns the token source for
|
||||
// "Application Default Credentials".
|
||||
// It is a shortcut for FindDefaultCredentials(ctx, scope).TokenSource.
|
||||
func DefaultTokenSource(ctx context.Context, scope ...string) (oauth2.TokenSource, error) {
|
||||
creds, err := FindDefaultCredentials(ctx, scope...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return creds.TokenSource, nil
|
||||
}
|
||||
|
||||
// FindDefaultCredentials searches for "Application Default Credentials".
|
||||
//
|
||||
// It looks for credentials in the following places,
|
||||
// preferring the first location found:
|
||||
//
|
||||
// 1. A JSON file whose path is specified by the
|
||||
// GOOGLE_APPLICATION_CREDENTIALS environment variable.
|
||||
// 2. A JSON file in a location known to the gcloud command-line tool.
|
||||
// On Windows, this is %APPDATA%/gcloud/application_default_credentials.json.
|
||||
// On other systems, $HOME/.config/gcloud/application_default_credentials.json.
|
||||
// 3. On Google App Engine it uses the appengine.AccessToken function.
|
||||
// 4. On Google Compute Engine and Google App Engine Managed VMs, it fetches
|
||||
// credentials from the metadata server.
|
||||
// (In this final case any provided scopes are ignored.)
|
||||
func FindDefaultCredentials(ctx context.Context, scope ...string) (*DefaultCredentials, error) {
|
||||
// First, try the environment variable.
|
||||
const envVar = "GOOGLE_APPLICATION_CREDENTIALS"
|
||||
if filename := os.Getenv(envVar); filename != "" {
|
||||
creds, err := readCredentialsFile(ctx, filename, scope)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("google: error getting credentials using %v environment variable: %v", envVar, err)
|
||||
}
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
// Second, try a well-known file.
|
||||
filename := wellKnownFile()
|
||||
if creds, err := readCredentialsFile(ctx, filename, scope); err == nil {
|
||||
return creds, nil
|
||||
} else if !os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("google: error getting credentials using well-known file (%v): %v", filename, err)
|
||||
}
|
||||
|
||||
// Third, if we're on Google App Engine use those credentials.
|
||||
if appengineTokenFunc != nil && !appengineFlex {
|
||||
return &DefaultCredentials{
|
||||
ProjectID: appengineAppIDFunc(ctx),
|
||||
TokenSource: AppEngineTokenSource(ctx, scope...),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Fourth, if we're on Google Compute Engine use the metadata server.
|
||||
if metadata.OnGCE() {
|
||||
id, _ := metadata.ProjectID()
|
||||
return &DefaultCredentials{
|
||||
ProjectID: id,
|
||||
TokenSource: ComputeTokenSource(""),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// None are found; return helpful error.
|
||||
const url = "https://developers.google.com/accounts/docs/application-default-credentials"
|
||||
return nil, fmt.Errorf("google: could not find default credentials. See %v for more information.", url)
|
||||
}
|
||||
|
||||
func wellKnownFile() string {
|
||||
const f = "application_default_credentials.json"
|
||||
if runtime.GOOS == "windows" {
|
||||
return filepath.Join(os.Getenv("APPDATA"), "gcloud", f)
|
||||
}
|
||||
return filepath.Join(guessUnixHomeDir(), ".config", "gcloud", f)
|
||||
}
|
||||
|
||||
func readCredentialsFile(ctx context.Context, filename string, scopes []string) (*DefaultCredentials, error) {
|
||||
b, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var f credentialsFile
|
||||
if err := json.Unmarshal(b, &f); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ts, err := f.tokenSource(ctx, append([]string(nil), scopes...))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &DefaultCredentials{
|
||||
ProjectID: f.ProjectID,
|
||||
TokenSource: ts,
|
||||
JSON: b,
|
||||
}, nil
|
||||
}
|
||||
150
vendor/golang.org/x/oauth2/google/example_test.go
generated
vendored
Normal file
150
vendor/golang.org/x/oauth2/google/example_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
// 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 appenginevm appengine
|
||||
|
||||
package google_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
"golang.org/x/oauth2/jwt"
|
||||
"google.golang.org/appengine"
|
||||
"google.golang.org/appengine/urlfetch"
|
||||
)
|
||||
|
||||
func ExampleDefaultClient() {
|
||||
client, err := google.DefaultClient(oauth2.NoContext,
|
||||
"https://www.googleapis.com/auth/devstorage.full_control")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
client.Get("...")
|
||||
}
|
||||
|
||||
func Example_webServer() {
|
||||
// Your credentials should be obtained from the Google
|
||||
// Developer Console (https://console.developers.google.com).
|
||||
conf := &oauth2.Config{
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
ClientSecret: "YOUR_CLIENT_SECRET",
|
||||
RedirectURL: "YOUR_REDIRECT_URL",
|
||||
Scopes: []string{
|
||||
"https://www.googleapis.com/auth/bigquery",
|
||||
"https://www.googleapis.com/auth/blogger",
|
||||
},
|
||||
Endpoint: google.Endpoint,
|
||||
}
|
||||
// Redirect user to Google's consent page to ask for permission
|
||||
// for the scopes specified above.
|
||||
url := conf.AuthCodeURL("state")
|
||||
fmt.Printf("Visit the URL for the auth dialog: %v", url)
|
||||
|
||||
// Handle the exchange code to initiate a transport.
|
||||
tok, err := conf.Exchange(oauth2.NoContext, "authorization-code")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
client := conf.Client(oauth2.NoContext, tok)
|
||||
client.Get("...")
|
||||
}
|
||||
|
||||
func ExampleJWTConfigFromJSON() {
|
||||
// Your credentials should be obtained from the Google
|
||||
// Developer Console (https://console.developers.google.com).
|
||||
// Navigate to your project, then see the "Credentials" page
|
||||
// under "APIs & Auth".
|
||||
// To create a service account client, click "Create new Client ID",
|
||||
// select "Service Account", and click "Create Client ID". A JSON
|
||||
// key file will then be downloaded to your computer.
|
||||
data, err := ioutil.ReadFile("/path/to/your-project-key.json")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
conf, err := google.JWTConfigFromJSON(data, "https://www.googleapis.com/auth/bigquery")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// Initiate an http.Client. The following GET request will be
|
||||
// authorized and authenticated on the behalf of
|
||||
// your service account.
|
||||
client := conf.Client(oauth2.NoContext)
|
||||
client.Get("...")
|
||||
}
|
||||
|
||||
func ExampleSDKConfig() {
|
||||
// The credentials will be obtained from the first account that
|
||||
// has been authorized with `gcloud auth login`.
|
||||
conf, err := google.NewSDKConfig("")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// Initiate an http.Client. The following GET request will be
|
||||
// authorized and authenticated on the behalf of the SDK user.
|
||||
client := conf.Client(oauth2.NoContext)
|
||||
client.Get("...")
|
||||
}
|
||||
|
||||
func Example_serviceAccount() {
|
||||
// Your credentials should be obtained from the Google
|
||||
// Developer Console (https://console.developers.google.com).
|
||||
conf := &jwt.Config{
|
||||
Email: "xxx@developer.gserviceaccount.com",
|
||||
// The contents of your RSA private key or your PEM file
|
||||
// that contains a private key.
|
||||
// If you have a p12 file instead, you
|
||||
// can use `openssl` to export the private key into a pem file.
|
||||
//
|
||||
// $ openssl pkcs12 -in key.p12 -passin pass:notasecret -out key.pem -nodes
|
||||
//
|
||||
// The field only supports PEM containers with no passphrase.
|
||||
// The openssl command will convert p12 keys to passphrase-less PEM containers.
|
||||
PrivateKey: []byte("-----BEGIN RSA PRIVATE KEY-----..."),
|
||||
Scopes: []string{
|
||||
"https://www.googleapis.com/auth/bigquery",
|
||||
"https://www.googleapis.com/auth/blogger",
|
||||
},
|
||||
TokenURL: google.JWTTokenURL,
|
||||
// If you would like to impersonate a user, you can
|
||||
// create a transport with a subject. The following GET
|
||||
// request will be made on the behalf of user@example.com.
|
||||
// Optional.
|
||||
Subject: "user@example.com",
|
||||
}
|
||||
// Initiate an http.Client, the following GET request will be
|
||||
// authorized and authenticated on the behalf of user@example.com.
|
||||
client := conf.Client(oauth2.NoContext)
|
||||
client.Get("...")
|
||||
}
|
||||
|
||||
func ExampleAppEngineTokenSource() {
|
||||
var req *http.Request // from the ServeHTTP handler
|
||||
ctx := appengine.NewContext(req)
|
||||
client := &http.Client{
|
||||
Transport: &oauth2.Transport{
|
||||
Source: google.AppEngineTokenSource(ctx, "https://www.googleapis.com/auth/bigquery"),
|
||||
Base: &urlfetch.Transport{
|
||||
Context: ctx,
|
||||
},
|
||||
},
|
||||
}
|
||||
client.Get("...")
|
||||
}
|
||||
|
||||
func ExampleComputeTokenSource() {
|
||||
client := &http.Client{
|
||||
Transport: &oauth2.Transport{
|
||||
// Fetch from Google Compute Engine's metadata server to retrieve
|
||||
// an access token for the provided account.
|
||||
// If no account is specified, "default" is used.
|
||||
Source: google.ComputeTokenSource(""),
|
||||
},
|
||||
}
|
||||
client.Get("...")
|
||||
}
|
||||
202
vendor/golang.org/x/oauth2/google/google.go
generated
vendored
Normal file
202
vendor/golang.org/x/oauth2/google/google.go
generated
vendored
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package google provides support for making OAuth2 authorized and
|
||||
// authenticated HTTP requests to Google APIs.
|
||||
// It supports the Web server flow, client-side credentials, service accounts,
|
||||
// Google Compute Engine service accounts, and Google App Engine service
|
||||
// accounts.
|
||||
//
|
||||
// For more information, please read
|
||||
// https://developers.google.com/accounts/docs/OAuth2
|
||||
// and
|
||||
// https://developers.google.com/accounts/docs/application-default-credentials.
|
||||
package google // import "golang.org/x/oauth2/google"
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"cloud.google.com/go/compute/metadata"
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/jwt"
|
||||
)
|
||||
|
||||
// Endpoint is Google's OAuth 2.0 endpoint.
|
||||
var Endpoint = oauth2.Endpoint{
|
||||
AuthURL: "https://accounts.google.com/o/oauth2/auth",
|
||||
TokenURL: "https://accounts.google.com/o/oauth2/token",
|
||||
}
|
||||
|
||||
// JWTTokenURL is Google's OAuth 2.0 token URL to use with the JWT flow.
|
||||
const JWTTokenURL = "https://accounts.google.com/o/oauth2/token"
|
||||
|
||||
// ConfigFromJSON uses a Google Developers Console client_credentials.json
|
||||
// file to construct a config.
|
||||
// client_credentials.json can be downloaded from
|
||||
// https://console.developers.google.com, under "Credentials". Download the Web
|
||||
// application credentials in the JSON format and provide the contents of the
|
||||
// file as jsonKey.
|
||||
func ConfigFromJSON(jsonKey []byte, scope ...string) (*oauth2.Config, error) {
|
||||
type cred struct {
|
||||
ClientID string `json:"client_id"`
|
||||
ClientSecret string `json:"client_secret"`
|
||||
RedirectURIs []string `json:"redirect_uris"`
|
||||
AuthURI string `json:"auth_uri"`
|
||||
TokenURI string `json:"token_uri"`
|
||||
}
|
||||
var j struct {
|
||||
Web *cred `json:"web"`
|
||||
Installed *cred `json:"installed"`
|
||||
}
|
||||
if err := json.Unmarshal(jsonKey, &j); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var c *cred
|
||||
switch {
|
||||
case j.Web != nil:
|
||||
c = j.Web
|
||||
case j.Installed != nil:
|
||||
c = j.Installed
|
||||
default:
|
||||
return nil, fmt.Errorf("oauth2/google: no credentials found")
|
||||
}
|
||||
if len(c.RedirectURIs) < 1 {
|
||||
return nil, errors.New("oauth2/google: missing redirect URL in the client_credentials.json")
|
||||
}
|
||||
return &oauth2.Config{
|
||||
ClientID: c.ClientID,
|
||||
ClientSecret: c.ClientSecret,
|
||||
RedirectURL: c.RedirectURIs[0],
|
||||
Scopes: scope,
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: c.AuthURI,
|
||||
TokenURL: c.TokenURI,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// JWTConfigFromJSON uses a Google Developers service account JSON key file to read
|
||||
// the credentials that authorize and authenticate the requests.
|
||||
// Create a service account on "Credentials" for your project at
|
||||
// https://console.developers.google.com to download a JSON key file.
|
||||
func JWTConfigFromJSON(jsonKey []byte, scope ...string) (*jwt.Config, error) {
|
||||
var f credentialsFile
|
||||
if err := json.Unmarshal(jsonKey, &f); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if f.Type != serviceAccountKey {
|
||||
return nil, fmt.Errorf("google: read JWT from JSON credentials: 'type' field is %q (expected %q)", f.Type, serviceAccountKey)
|
||||
}
|
||||
scope = append([]string(nil), scope...) // copy
|
||||
return f.jwtConfig(scope), nil
|
||||
}
|
||||
|
||||
// JSON key file types.
|
||||
const (
|
||||
serviceAccountKey = "service_account"
|
||||
userCredentialsKey = "authorized_user"
|
||||
)
|
||||
|
||||
// credentialsFile is the unmarshalled representation of a credentials file.
|
||||
type credentialsFile struct {
|
||||
Type string `json:"type"` // serviceAccountKey or userCredentialsKey
|
||||
|
||||
// Service Account fields
|
||||
ClientEmail string `json:"client_email"`
|
||||
PrivateKeyID string `json:"private_key_id"`
|
||||
PrivateKey string `json:"private_key"`
|
||||
TokenURL string `json:"token_uri"`
|
||||
ProjectID string `json:"project_id"`
|
||||
|
||||
// User Credential fields
|
||||
// (These typically come from gcloud auth.)
|
||||
ClientSecret string `json:"client_secret"`
|
||||
ClientID string `json:"client_id"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
}
|
||||
|
||||
func (f *credentialsFile) jwtConfig(scopes []string) *jwt.Config {
|
||||
cfg := &jwt.Config{
|
||||
Email: f.ClientEmail,
|
||||
PrivateKey: []byte(f.PrivateKey),
|
||||
PrivateKeyID: f.PrivateKeyID,
|
||||
Scopes: scopes,
|
||||
TokenURL: f.TokenURL,
|
||||
}
|
||||
if cfg.TokenURL == "" {
|
||||
cfg.TokenURL = JWTTokenURL
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
func (f *credentialsFile) tokenSource(ctx context.Context, scopes []string) (oauth2.TokenSource, error) {
|
||||
switch f.Type {
|
||||
case serviceAccountKey:
|
||||
cfg := f.jwtConfig(scopes)
|
||||
return cfg.TokenSource(ctx), nil
|
||||
case userCredentialsKey:
|
||||
cfg := &oauth2.Config{
|
||||
ClientID: f.ClientID,
|
||||
ClientSecret: f.ClientSecret,
|
||||
Scopes: scopes,
|
||||
Endpoint: Endpoint,
|
||||
}
|
||||
tok := &oauth2.Token{RefreshToken: f.RefreshToken}
|
||||
return cfg.TokenSource(ctx, tok), nil
|
||||
case "":
|
||||
return nil, errors.New("missing 'type' field in credentials")
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown credential type: %q", f.Type)
|
||||
}
|
||||
}
|
||||
|
||||
// ComputeTokenSource returns a token source that fetches access tokens
|
||||
// from Google Compute Engine (GCE)'s metadata server. It's only valid to use
|
||||
// this token source if your program is running on a GCE instance.
|
||||
// If no account is specified, "default" is used.
|
||||
// Further information about retrieving access tokens from the GCE metadata
|
||||
// server can be found at https://cloud.google.com/compute/docs/authentication.
|
||||
func ComputeTokenSource(account string) oauth2.TokenSource {
|
||||
return oauth2.ReuseTokenSource(nil, computeSource{account: account})
|
||||
}
|
||||
|
||||
type computeSource struct {
|
||||
account string
|
||||
}
|
||||
|
||||
func (cs computeSource) Token() (*oauth2.Token, error) {
|
||||
if !metadata.OnGCE() {
|
||||
return nil, errors.New("oauth2/google: can't get a token from the metadata service; not running on GCE")
|
||||
}
|
||||
acct := cs.account
|
||||
if acct == "" {
|
||||
acct = "default"
|
||||
}
|
||||
tokenJSON, err := metadata.Get("instance/service-accounts/" + acct + "/token")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var res struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
ExpiresInSec int `json:"expires_in"`
|
||||
TokenType string `json:"token_type"`
|
||||
}
|
||||
err = json.NewDecoder(strings.NewReader(tokenJSON)).Decode(&res)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("oauth2/google: invalid token JSON from metadata: %v", err)
|
||||
}
|
||||
if res.ExpiresInSec == 0 || res.AccessToken == "" {
|
||||
return nil, fmt.Errorf("oauth2/google: incomplete token received from metadata")
|
||||
}
|
||||
return &oauth2.Token{
|
||||
AccessToken: res.AccessToken,
|
||||
TokenType: res.TokenType,
|
||||
Expiry: time.Now().Add(time.Duration(res.ExpiresInSec) * time.Second),
|
||||
}, nil
|
||||
}
|
||||
116
vendor/golang.org/x/oauth2/google/google_test.go
generated
vendored
Normal file
116
vendor/golang.org/x/oauth2/google/google_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
// 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 google
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var webJSONKey = []byte(`
|
||||
{
|
||||
"web": {
|
||||
"auth_uri": "https://google.com/o/oauth2/auth",
|
||||
"client_secret": "3Oknc4jS_wA2r9i",
|
||||
"token_uri": "https://google.com/o/oauth2/token",
|
||||
"client_email": "222-nprqovg5k43uum874cs9osjt2koe97g8@developer.gserviceaccount.com",
|
||||
"redirect_uris": ["https://www.example.com/oauth2callback"],
|
||||
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/222-nprqovg5k43uum874cs9osjt2koe97g8@developer.gserviceaccount.com",
|
||||
"client_id": "222-nprqovg5k43uum874cs9osjt2koe97g8.apps.googleusercontent.com",
|
||||
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
||||
"javascript_origins": ["https://www.example.com"]
|
||||
}
|
||||
}`)
|
||||
|
||||
var installedJSONKey = []byte(`{
|
||||
"installed": {
|
||||
"client_id": "222-installed.apps.googleusercontent.com",
|
||||
"redirect_uris": ["https://www.example.com/oauth2callback"]
|
||||
}
|
||||
}`)
|
||||
|
||||
var jwtJSONKey = []byte(`{
|
||||
"private_key_id": "268f54e43a1af97cfc71731688434f45aca15c8b",
|
||||
"private_key": "super secret key",
|
||||
"client_email": "gopher@developer.gserviceaccount.com",
|
||||
"client_id": "gopher.apps.googleusercontent.com",
|
||||
"token_uri": "https://accounts.google.com/o/gophers/token",
|
||||
"type": "service_account"
|
||||
}`)
|
||||
|
||||
var jwtJSONKeyNoTokenURL = []byte(`{
|
||||
"private_key_id": "268f54e43a1af97cfc71731688434f45aca15c8b",
|
||||
"private_key": "super secret key",
|
||||
"client_email": "gopher@developer.gserviceaccount.com",
|
||||
"client_id": "gopher.apps.googleusercontent.com",
|
||||
"type": "service_account"
|
||||
}`)
|
||||
|
||||
func TestConfigFromJSON(t *testing.T) {
|
||||
conf, err := ConfigFromJSON(webJSONKey, "scope1", "scope2")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if got, want := conf.ClientID, "222-nprqovg5k43uum874cs9osjt2koe97g8.apps.googleusercontent.com"; got != want {
|
||||
t.Errorf("ClientID = %q; want %q", got, want)
|
||||
}
|
||||
if got, want := conf.ClientSecret, "3Oknc4jS_wA2r9i"; got != want {
|
||||
t.Errorf("ClientSecret = %q; want %q", got, want)
|
||||
}
|
||||
if got, want := conf.RedirectURL, "https://www.example.com/oauth2callback"; got != want {
|
||||
t.Errorf("RedictURL = %q; want %q", got, want)
|
||||
}
|
||||
if got, want := strings.Join(conf.Scopes, ","), "scope1,scope2"; got != want {
|
||||
t.Errorf("Scopes = %q; want %q", got, want)
|
||||
}
|
||||
if got, want := conf.Endpoint.AuthURL, "https://google.com/o/oauth2/auth"; got != want {
|
||||
t.Errorf("AuthURL = %q; want %q", got, want)
|
||||
}
|
||||
if got, want := conf.Endpoint.TokenURL, "https://google.com/o/oauth2/token"; got != want {
|
||||
t.Errorf("TokenURL = %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigFromJSON_Installed(t *testing.T) {
|
||||
conf, err := ConfigFromJSON(installedJSONKey)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if got, want := conf.ClientID, "222-installed.apps.googleusercontent.com"; got != want {
|
||||
t.Errorf("ClientID = %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJWTConfigFromJSON(t *testing.T) {
|
||||
conf, err := JWTConfigFromJSON(jwtJSONKey, "scope1", "scope2")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got, want := conf.Email, "gopher@developer.gserviceaccount.com"; got != want {
|
||||
t.Errorf("Email = %q, want %q", got, want)
|
||||
}
|
||||
if got, want := string(conf.PrivateKey), "super secret key"; got != want {
|
||||
t.Errorf("PrivateKey = %q, want %q", got, want)
|
||||
}
|
||||
if got, want := conf.PrivateKeyID, "268f54e43a1af97cfc71731688434f45aca15c8b"; got != want {
|
||||
t.Errorf("PrivateKeyID = %q, want %q", got, want)
|
||||
}
|
||||
if got, want := strings.Join(conf.Scopes, ","), "scope1,scope2"; got != want {
|
||||
t.Errorf("Scopes = %q; want %q", got, want)
|
||||
}
|
||||
if got, want := conf.TokenURL, "https://accounts.google.com/o/gophers/token"; got != want {
|
||||
t.Errorf("TokenURL = %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJWTConfigFromJSONNoTokenURL(t *testing.T) {
|
||||
conf, err := JWTConfigFromJSON(jwtJSONKeyNoTokenURL, "scope1", "scope2")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got, want := conf.TokenURL, "https://accounts.google.com/o/oauth2/token"; got != want {
|
||||
t.Errorf("TokenURL = %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
74
vendor/golang.org/x/oauth2/google/jwt.go
generated
vendored
Normal file
74
vendor/golang.org/x/oauth2/google/jwt.go
generated
vendored
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
// 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 google
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/internal"
|
||||
"golang.org/x/oauth2/jws"
|
||||
)
|
||||
|
||||
// JWTAccessTokenSourceFromJSON uses a Google Developers service account JSON
|
||||
// key file to read the credentials that authorize and authenticate the
|
||||
// requests, and returns a TokenSource that does not use any OAuth2 flow but
|
||||
// instead creates a JWT and sends that as the access token.
|
||||
// The audience is typically a URL that specifies the scope of the credentials.
|
||||
//
|
||||
// Note that this is not a standard OAuth flow, but rather an
|
||||
// optimization supported by a few Google services.
|
||||
// Unless you know otherwise, you should use JWTConfigFromJSON instead.
|
||||
func JWTAccessTokenSourceFromJSON(jsonKey []byte, audience string) (oauth2.TokenSource, error) {
|
||||
cfg, err := JWTConfigFromJSON(jsonKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("google: could not parse JSON key: %v", err)
|
||||
}
|
||||
pk, err := internal.ParseKey(cfg.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("google: could not parse key: %v", err)
|
||||
}
|
||||
ts := &jwtAccessTokenSource{
|
||||
email: cfg.Email,
|
||||
audience: audience,
|
||||
pk: pk,
|
||||
pkID: cfg.PrivateKeyID,
|
||||
}
|
||||
tok, err := ts.Token()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return oauth2.ReuseTokenSource(tok, ts), nil
|
||||
}
|
||||
|
||||
type jwtAccessTokenSource struct {
|
||||
email, audience string
|
||||
pk *rsa.PrivateKey
|
||||
pkID string
|
||||
}
|
||||
|
||||
func (ts *jwtAccessTokenSource) Token() (*oauth2.Token, error) {
|
||||
iat := time.Now()
|
||||
exp := iat.Add(time.Hour)
|
||||
cs := &jws.ClaimSet{
|
||||
Iss: ts.email,
|
||||
Sub: ts.email,
|
||||
Aud: ts.audience,
|
||||
Iat: iat.Unix(),
|
||||
Exp: exp.Unix(),
|
||||
}
|
||||
hdr := &jws.Header{
|
||||
Algorithm: "RS256",
|
||||
Typ: "JWT",
|
||||
KeyID: string(ts.pkID),
|
||||
}
|
||||
msg, err := jws.Encode(hdr, cs, ts.pk)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("google: could not encode JWT: %v", err)
|
||||
}
|
||||
return &oauth2.Token{AccessToken: msg, TokenType: "Bearer", Expiry: exp}, nil
|
||||
}
|
||||
91
vendor/golang.org/x/oauth2/google/jwt_test.go
generated
vendored
Normal file
91
vendor/golang.org/x/oauth2/google/jwt_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
// 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 google
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/oauth2/jws"
|
||||
)
|
||||
|
||||
func TestJWTAccessTokenSourceFromJSON(t *testing.T) {
|
||||
// Generate a key we can use in the test data.
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Encode the key and substitute into our example JSON.
|
||||
enc := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "PRIVATE KEY",
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
|
||||
})
|
||||
enc, err = json.Marshal(string(enc))
|
||||
if err != nil {
|
||||
t.Fatalf("json.Marshal: %v", err)
|
||||
}
|
||||
jsonKey := bytes.Replace(jwtJSONKey, []byte(`"super secret key"`), enc, 1)
|
||||
|
||||
ts, err := JWTAccessTokenSourceFromJSON(jsonKey, "audience")
|
||||
if err != nil {
|
||||
t.Fatalf("JWTAccessTokenSourceFromJSON: %v\nJSON: %s", err, string(jsonKey))
|
||||
}
|
||||
|
||||
tok, err := ts.Token()
|
||||
if err != nil {
|
||||
t.Fatalf("Token: %v", err)
|
||||
}
|
||||
|
||||
if got, want := tok.TokenType, "Bearer"; got != want {
|
||||
t.Errorf("TokenType = %q, want %q", got, want)
|
||||
}
|
||||
if got := tok.Expiry; tok.Expiry.Before(time.Now()) {
|
||||
t.Errorf("Expiry = %v, should not be expired", got)
|
||||
}
|
||||
|
||||
err = jws.Verify(tok.AccessToken, &privateKey.PublicKey)
|
||||
if err != nil {
|
||||
t.Errorf("jws.Verify on AccessToken: %v", err)
|
||||
}
|
||||
|
||||
claim, err := jws.Decode(tok.AccessToken)
|
||||
if err != nil {
|
||||
t.Fatalf("jws.Decode on AccessToken: %v", err)
|
||||
}
|
||||
|
||||
if got, want := claim.Iss, "gopher@developer.gserviceaccount.com"; got != want {
|
||||
t.Errorf("Iss = %q, want %q", got, want)
|
||||
}
|
||||
if got, want := claim.Sub, "gopher@developer.gserviceaccount.com"; got != want {
|
||||
t.Errorf("Sub = %q, want %q", got, want)
|
||||
}
|
||||
if got, want := claim.Aud, "audience"; got != want {
|
||||
t.Errorf("Aud = %q, want %q", got, want)
|
||||
}
|
||||
|
||||
// Finally, check the header private key.
|
||||
parts := strings.Split(tok.AccessToken, ".")
|
||||
hdrJSON, err := base64.RawURLEncoding.DecodeString(parts[0])
|
||||
if err != nil {
|
||||
t.Fatalf("base64 DecodeString: %v\nString: %q", err, parts[0])
|
||||
}
|
||||
var hdr jws.Header
|
||||
if err := json.Unmarshal([]byte(hdrJSON), &hdr); err != nil {
|
||||
t.Fatalf("json.Unmarshal: %v (%q)", err, hdrJSON)
|
||||
}
|
||||
|
||||
if got, want := hdr.KeyID, "268f54e43a1af97cfc71731688434f45aca15c8b"; got != want {
|
||||
t.Errorf("Header KeyID = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
172
vendor/golang.org/x/oauth2/google/sdk.go
generated
vendored
Normal file
172
vendor/golang.org/x/oauth2/google/sdk.go
generated
vendored
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
// 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 google
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/internal"
|
||||
)
|
||||
|
||||
type sdkCredentials struct {
|
||||
Data []struct {
|
||||
Credential struct {
|
||||
ClientID string `json:"client_id"`
|
||||
ClientSecret string `json:"client_secret"`
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
TokenExpiry *time.Time `json:"token_expiry"`
|
||||
} `json:"credential"`
|
||||
Key struct {
|
||||
Account string `json:"account"`
|
||||
Scope string `json:"scope"`
|
||||
} `json:"key"`
|
||||
}
|
||||
}
|
||||
|
||||
// An SDKConfig provides access to tokens from an account already
|
||||
// authorized via the Google Cloud SDK.
|
||||
type SDKConfig struct {
|
||||
conf oauth2.Config
|
||||
initialToken *oauth2.Token
|
||||
}
|
||||
|
||||
// NewSDKConfig creates an SDKConfig for the given Google Cloud SDK
|
||||
// account. If account is empty, the account currently active in
|
||||
// Google Cloud SDK properties is used.
|
||||
// Google Cloud SDK credentials must be created by running `gcloud auth`
|
||||
// before using this function.
|
||||
// The Google Cloud SDK is available at https://cloud.google.com/sdk/.
|
||||
func NewSDKConfig(account string) (*SDKConfig, error) {
|
||||
configPath, err := sdkConfigPath()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("oauth2/google: error getting SDK config path: %v", err)
|
||||
}
|
||||
credentialsPath := filepath.Join(configPath, "credentials")
|
||||
f, err := os.Open(credentialsPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("oauth2/google: failed to load SDK credentials: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var c sdkCredentials
|
||||
if err := json.NewDecoder(f).Decode(&c); err != nil {
|
||||
return nil, fmt.Errorf("oauth2/google: failed to decode SDK credentials from %q: %v", credentialsPath, err)
|
||||
}
|
||||
if len(c.Data) == 0 {
|
||||
return nil, fmt.Errorf("oauth2/google: no credentials found in %q, run `gcloud auth login` to create one", credentialsPath)
|
||||
}
|
||||
if account == "" {
|
||||
propertiesPath := filepath.Join(configPath, "properties")
|
||||
f, err := os.Open(propertiesPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("oauth2/google: failed to load SDK properties: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
ini, err := internal.ParseINI(f)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("oauth2/google: failed to parse SDK properties %q: %v", propertiesPath, err)
|
||||
}
|
||||
core, ok := ini["core"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("oauth2/google: failed to find [core] section in %v", ini)
|
||||
}
|
||||
active, ok := core["account"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("oauth2/google: failed to find %q attribute in %v", "account", core)
|
||||
}
|
||||
account = active
|
||||
}
|
||||
|
||||
for _, d := range c.Data {
|
||||
if account == "" || d.Key.Account == account {
|
||||
if d.Credential.AccessToken == "" && d.Credential.RefreshToken == "" {
|
||||
return nil, fmt.Errorf("oauth2/google: no token available for account %q", account)
|
||||
}
|
||||
var expiry time.Time
|
||||
if d.Credential.TokenExpiry != nil {
|
||||
expiry = *d.Credential.TokenExpiry
|
||||
}
|
||||
return &SDKConfig{
|
||||
conf: oauth2.Config{
|
||||
ClientID: d.Credential.ClientID,
|
||||
ClientSecret: d.Credential.ClientSecret,
|
||||
Scopes: strings.Split(d.Key.Scope, " "),
|
||||
Endpoint: Endpoint,
|
||||
RedirectURL: "oob",
|
||||
},
|
||||
initialToken: &oauth2.Token{
|
||||
AccessToken: d.Credential.AccessToken,
|
||||
RefreshToken: d.Credential.RefreshToken,
|
||||
Expiry: expiry,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("oauth2/google: no such credentials for account %q", account)
|
||||
}
|
||||
|
||||
// Client returns an HTTP client using Google Cloud SDK credentials to
|
||||
// authorize requests. The token will auto-refresh as necessary. The
|
||||
// underlying http.RoundTripper will be obtained using the provided
|
||||
// context. The returned client and its Transport should not be
|
||||
// modified.
|
||||
func (c *SDKConfig) Client(ctx context.Context) *http.Client {
|
||||
return &http.Client{
|
||||
Transport: &oauth2.Transport{
|
||||
Source: c.TokenSource(ctx),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// TokenSource returns an oauth2.TokenSource that retrieve tokens from
|
||||
// Google Cloud SDK credentials using the provided context.
|
||||
// It will returns the current access token stored in the credentials,
|
||||
// and refresh it when it expires, but it won't update the credentials
|
||||
// with the new access token.
|
||||
func (c *SDKConfig) TokenSource(ctx context.Context) oauth2.TokenSource {
|
||||
return c.conf.TokenSource(ctx, c.initialToken)
|
||||
}
|
||||
|
||||
// Scopes are the OAuth 2.0 scopes the current account is authorized for.
|
||||
func (c *SDKConfig) Scopes() []string {
|
||||
return c.conf.Scopes
|
||||
}
|
||||
|
||||
// sdkConfigPath tries to guess where the gcloud config is located.
|
||||
// It can be overridden during tests.
|
||||
var sdkConfigPath = func() (string, error) {
|
||||
if runtime.GOOS == "windows" {
|
||||
return filepath.Join(os.Getenv("APPDATA"), "gcloud"), nil
|
||||
}
|
||||
homeDir := guessUnixHomeDir()
|
||||
if homeDir == "" {
|
||||
return "", errors.New("unable to get current user home directory: os/user lookup failed; $HOME is empty")
|
||||
}
|
||||
return filepath.Join(homeDir, ".config", "gcloud"), nil
|
||||
}
|
||||
|
||||
func guessUnixHomeDir() string {
|
||||
// Prefer $HOME over user.Current due to glibc bug: golang.org/issue/13470
|
||||
if v := os.Getenv("HOME"); v != "" {
|
||||
return v
|
||||
}
|
||||
// Else, fall back to user.Current:
|
||||
if u, err := user.Current(); err == nil {
|
||||
return u.HomeDir
|
||||
}
|
||||
return ""
|
||||
}
|
||||
46
vendor/golang.org/x/oauth2/google/sdk_test.go
generated
vendored
Normal file
46
vendor/golang.org/x/oauth2/google/sdk_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
// 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 google
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestSDKConfig(t *testing.T) {
|
||||
sdkConfigPath = func() (string, error) {
|
||||
return "testdata/gcloud", nil
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
account string
|
||||
accessToken string
|
||||
err bool
|
||||
}{
|
||||
{"", "bar_access_token", false},
|
||||
{"foo@example.com", "foo_access_token", false},
|
||||
{"bar@example.com", "bar_access_token", false},
|
||||
{"baz@serviceaccount.example.com", "", true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
c, err := NewSDKConfig(tt.account)
|
||||
if got, want := err != nil, tt.err; got != want {
|
||||
if !tt.err {
|
||||
t.Errorf("got %v, want nil", err)
|
||||
} else {
|
||||
t.Errorf("got nil, want error")
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
tok := c.initialToken
|
||||
if tok == nil {
|
||||
t.Errorf("got nil, want %q", tt.accessToken)
|
||||
continue
|
||||
}
|
||||
if tok.AccessToken != tt.accessToken {
|
||||
t.Errorf("got %q, want %q", tok.AccessToken, tt.accessToken)
|
||||
}
|
||||
}
|
||||
}
|
||||
6
vendor/golang.org/x/oauth2/internal/doc.go
generated
vendored
Normal file
6
vendor/golang.org/x/oauth2/internal/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
// Copyright 2017 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 internal contains support packages for oauth2 package.
|
||||
package internal
|
||||
75
vendor/golang.org/x/oauth2/internal/oauth2.go
generated
vendored
Normal file
75
vendor/golang.org/x/oauth2/internal/oauth2.go
generated
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ParseKey converts the binary contents of a private key file
|
||||
// to an *rsa.PrivateKey. It detects whether the private key is in a
|
||||
// PEM container or not. If so, it extracts the the private key
|
||||
// from PEM container before conversion. It only supports PEM
|
||||
// containers with no passphrase.
|
||||
func ParseKey(key []byte) (*rsa.PrivateKey, error) {
|
||||
block, _ := pem.Decode(key)
|
||||
if block != nil {
|
||||
key = block.Bytes
|
||||
}
|
||||
parsedKey, err := x509.ParsePKCS8PrivateKey(key)
|
||||
if err != nil {
|
||||
parsedKey, err = x509.ParsePKCS1PrivateKey(key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("private key should be a PEM or plain PKSC1 or PKCS8; parse error: %v", err)
|
||||
}
|
||||
}
|
||||
parsed, ok := parsedKey.(*rsa.PrivateKey)
|
||||
if !ok {
|
||||
return nil, errors.New("private key is invalid")
|
||||
}
|
||||
return parsed, nil
|
||||
}
|
||||
|
||||
func ParseINI(ini io.Reader) (map[string]map[string]string, error) {
|
||||
result := map[string]map[string]string{
|
||||
"": {}, // root section
|
||||
}
|
||||
scanner := bufio.NewScanner(ini)
|
||||
currentSection := ""
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if strings.HasPrefix(line, ";") {
|
||||
// comment.
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
|
||||
currentSection = strings.TrimSpace(line[1 : len(line)-1])
|
||||
result[currentSection] = map[string]string{}
|
||||
continue
|
||||
}
|
||||
parts := strings.SplitN(line, "=", 2)
|
||||
if len(parts) == 2 && parts[0] != "" {
|
||||
result[currentSection][strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
|
||||
}
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("error scanning ini: %v", err)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func CondVal(v string) []string {
|
||||
if v == "" {
|
||||
return nil
|
||||
}
|
||||
return []string{v}
|
||||
}
|
||||
61
vendor/golang.org/x/oauth2/internal/oauth2_test.go
generated
vendored
Normal file
61
vendor/golang.org/x/oauth2/internal/oauth2_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseINI(t *testing.T) {
|
||||
tests := []struct {
|
||||
ini string
|
||||
want map[string]map[string]string
|
||||
}{
|
||||
{
|
||||
`root = toor
|
||||
[foo]
|
||||
bar = hop
|
||||
ini = nin
|
||||
`,
|
||||
map[string]map[string]string{
|
||||
"": {"root": "toor"},
|
||||
"foo": {"bar": "hop", "ini": "nin"},
|
||||
},
|
||||
},
|
||||
{
|
||||
`[empty]
|
||||
[section]
|
||||
empty=
|
||||
`,
|
||||
map[string]map[string]string{
|
||||
"": {},
|
||||
"empty": {},
|
||||
"section": {"empty": ""},
|
||||
},
|
||||
},
|
||||
{
|
||||
`ignore
|
||||
[invalid
|
||||
=stuff
|
||||
;comment=true
|
||||
`,
|
||||
map[string]map[string]string{
|
||||
"": {},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
result, err := ParseINI(strings.NewReader(tt.ini))
|
||||
if err != nil {
|
||||
t.Errorf("ParseINI(%q) error %v, want: no error", tt.ini, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(result, tt.want) {
|
||||
t.Errorf("ParseINI(%q) = %#v, want: %#v", tt.ini, result, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
250
vendor/golang.org/x/oauth2/internal/token.go
generated
vendored
Normal file
250
vendor/golang.org/x/oauth2/internal/token.go
generated
vendored
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/net/context/ctxhttp"
|
||||
)
|
||||
|
||||
// Token represents the crendentials used to authorize
|
||||
// the requests to access protected resources on the OAuth 2.0
|
||||
// provider's backend.
|
||||
//
|
||||
// This type is a mirror of oauth2.Token and exists to break
|
||||
// an otherwise-circular dependency. Other internal packages
|
||||
// should convert this Token into an oauth2.Token before use.
|
||||
type Token struct {
|
||||
// AccessToken is the token that authorizes and authenticates
|
||||
// the requests.
|
||||
AccessToken string
|
||||
|
||||
// TokenType is the type of token.
|
||||
// The Type method returns either this or "Bearer", the default.
|
||||
TokenType string
|
||||
|
||||
// RefreshToken is a token that's used by the application
|
||||
// (as opposed to the user) to refresh the access token
|
||||
// if it expires.
|
||||
RefreshToken string
|
||||
|
||||
// Expiry is the optional expiration time of the access token.
|
||||
//
|
||||
// If zero, TokenSource implementations will reuse the same
|
||||
// token forever and RefreshToken or equivalent
|
||||
// mechanisms for that TokenSource will not be used.
|
||||
Expiry time.Time
|
||||
|
||||
// Raw optionally contains extra metadata from the server
|
||||
// when updating a token.
|
||||
Raw interface{}
|
||||
}
|
||||
|
||||
// tokenJSON is the struct representing the HTTP response from OAuth2
|
||||
// providers returning a token in JSON form.
|
||||
type tokenJSON struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
TokenType string `json:"token_type"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
ExpiresIn expirationTime `json:"expires_in"` // at least PayPal returns string, while most return number
|
||||
Expires expirationTime `json:"expires"` // broken Facebook spelling of expires_in
|
||||
}
|
||||
|
||||
func (e *tokenJSON) expiry() (t time.Time) {
|
||||
if v := e.ExpiresIn; v != 0 {
|
||||
return time.Now().Add(time.Duration(v) * time.Second)
|
||||
}
|
||||
if v := e.Expires; v != 0 {
|
||||
return time.Now().Add(time.Duration(v) * time.Second)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type expirationTime int32
|
||||
|
||||
func (e *expirationTime) UnmarshalJSON(b []byte) error {
|
||||
var n json.Number
|
||||
err := json.Unmarshal(b, &n)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i, err := n.Int64()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*e = expirationTime(i)
|
||||
return nil
|
||||
}
|
||||
|
||||
var brokenAuthHeaderProviders = []string{
|
||||
"https://accounts.google.com/",
|
||||
"https://api.codeswholesale.com/oauth/token",
|
||||
"https://api.dropbox.com/",
|
||||
"https://api.dropboxapi.com/",
|
||||
"https://api.instagram.com/",
|
||||
"https://api.netatmo.net/",
|
||||
"https://api.odnoklassniki.ru/",
|
||||
"https://api.pushbullet.com/",
|
||||
"https://api.soundcloud.com/",
|
||||
"https://api.twitch.tv/",
|
||||
"https://app.box.com/",
|
||||
"https://connect.stripe.com/",
|
||||
"https://graph.facebook.com", // see https://github.com/golang/oauth2/issues/214
|
||||
"https://login.microsoftonline.com/",
|
||||
"https://login.salesforce.com/",
|
||||
"https://login.windows.net",
|
||||
"https://oauth.sandbox.trainingpeaks.com/",
|
||||
"https://oauth.trainingpeaks.com/",
|
||||
"https://oauth.vk.com/",
|
||||
"https://openapi.baidu.com/",
|
||||
"https://slack.com/",
|
||||
"https://test-sandbox.auth.corp.google.com",
|
||||
"https://test.salesforce.com/",
|
||||
"https://user.gini.net/",
|
||||
"https://www.douban.com/",
|
||||
"https://www.googleapis.com/",
|
||||
"https://www.linkedin.com/",
|
||||
"https://www.strava.com/oauth/",
|
||||
"https://www.wunderlist.com/oauth/",
|
||||
"https://api.patreon.com/",
|
||||
"https://sandbox.codeswholesale.com/oauth/token",
|
||||
"https://api.sipgate.com/v1/authorization/oauth",
|
||||
}
|
||||
|
||||
// brokenAuthHeaderDomains lists broken providers that issue dynamic endpoints.
|
||||
var brokenAuthHeaderDomains = []string{
|
||||
".force.com",
|
||||
".myshopify.com",
|
||||
".okta.com",
|
||||
".oktapreview.com",
|
||||
}
|
||||
|
||||
func RegisterBrokenAuthHeaderProvider(tokenURL string) {
|
||||
brokenAuthHeaderProviders = append(brokenAuthHeaderProviders, tokenURL)
|
||||
}
|
||||
|
||||
// providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL
|
||||
// implements the OAuth2 spec correctly
|
||||
// See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
|
||||
// In summary:
|
||||
// - Reddit only accepts client secret in the Authorization header
|
||||
// - Dropbox accepts either it in URL param or Auth header, but not both.
|
||||
// - Google only accepts URL param (not spec compliant?), not Auth header
|
||||
// - Stripe only accepts client secret in Auth header with Bearer method, not Basic
|
||||
func providerAuthHeaderWorks(tokenURL string) bool {
|
||||
for _, s := range brokenAuthHeaderProviders {
|
||||
if strings.HasPrefix(tokenURL, s) {
|
||||
// Some sites fail to implement the OAuth2 spec fully.
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if u, err := url.Parse(tokenURL); err == nil {
|
||||
for _, s := range brokenAuthHeaderDomains {
|
||||
if strings.HasSuffix(u.Host, s) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Assume the provider implements the spec properly
|
||||
// otherwise. We can add more exceptions as they're
|
||||
// discovered. We will _not_ be adding configurable hooks
|
||||
// to this package to let users select server bugs.
|
||||
return true
|
||||
}
|
||||
|
||||
func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string, v url.Values) (*Token, error) {
|
||||
hc, err := ContextClient(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bustedAuth := !providerAuthHeaderWorks(tokenURL)
|
||||
if bustedAuth {
|
||||
if clientID != "" {
|
||||
v.Set("client_id", clientID)
|
||||
}
|
||||
if clientSecret != "" {
|
||||
v.Set("client_secret", clientSecret)
|
||||
}
|
||||
}
|
||||
req, err := http.NewRequest("POST", tokenURL, strings.NewReader(v.Encode()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
if !bustedAuth {
|
||||
req.SetBasicAuth(url.QueryEscape(clientID), url.QueryEscape(clientSecret))
|
||||
}
|
||||
r, err := ctxhttp.Do(ctx, hc, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer r.Body.Close()
|
||||
body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1<<20))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
|
||||
}
|
||||
if code := r.StatusCode; code < 200 || code > 299 {
|
||||
return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", r.Status, body)
|
||||
}
|
||||
|
||||
var token *Token
|
||||
content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||
switch content {
|
||||
case "application/x-www-form-urlencoded", "text/plain":
|
||||
vals, err := url.ParseQuery(string(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
token = &Token{
|
||||
AccessToken: vals.Get("access_token"),
|
||||
TokenType: vals.Get("token_type"),
|
||||
RefreshToken: vals.Get("refresh_token"),
|
||||
Raw: vals,
|
||||
}
|
||||
e := vals.Get("expires_in")
|
||||
if e == "" {
|
||||
// TODO(jbd): Facebook's OAuth2 implementation is broken and
|
||||
// returns expires_in field in expires. Remove the fallback to expires,
|
||||
// when Facebook fixes their implementation.
|
||||
e = vals.Get("expires")
|
||||
}
|
||||
expires, _ := strconv.Atoi(e)
|
||||
if expires != 0 {
|
||||
token.Expiry = time.Now().Add(time.Duration(expires) * time.Second)
|
||||
}
|
||||
default:
|
||||
var tj tokenJSON
|
||||
if err = json.Unmarshal(body, &tj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
token = &Token{
|
||||
AccessToken: tj.AccessToken,
|
||||
TokenType: tj.TokenType,
|
||||
RefreshToken: tj.RefreshToken,
|
||||
Expiry: tj.expiry(),
|
||||
Raw: make(map[string]interface{}),
|
||||
}
|
||||
json.Unmarshal(body, &token.Raw) // no error checks for optional fields
|
||||
}
|
||||
// Don't overwrite `RefreshToken` with an empty value
|
||||
// if this was a token refreshing request.
|
||||
if token.RefreshToken == "" {
|
||||
token.RefreshToken = v.Get("refresh_token")
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
104
vendor/golang.org/x/oauth2/internal/token_test.go
generated
vendored
Normal file
104
vendor/golang.org/x/oauth2/internal/token_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func TestRegisterBrokenAuthHeaderProvider(t *testing.T) {
|
||||
RegisterBrokenAuthHeaderProvider("https://aaa.com/")
|
||||
tokenURL := "https://aaa.com/token"
|
||||
if providerAuthHeaderWorks(tokenURL) {
|
||||
t.Errorf("got %q as unbroken; want broken", tokenURL)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRetrieveTokenBustedNoSecret(t *testing.T) {
|
||||
const clientID = "client-id"
|
||||
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.FormValue("client_id"), clientID; got != want {
|
||||
t.Errorf("client_id = %q; want %q", got, want)
|
||||
}
|
||||
if got, want := r.FormValue("client_secret"), ""; got != want {
|
||||
t.Errorf("client_secret = %q; want empty", got)
|
||||
}
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
RegisterBrokenAuthHeaderProvider(ts.URL)
|
||||
_, err := RetrieveToken(context.Background(), clientID, "", ts.URL, url.Values{})
|
||||
if err != nil {
|
||||
t.Errorf("RetrieveToken = %v; want no error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_providerAuthHeaderWorks(t *testing.T) {
|
||||
for _, p := range brokenAuthHeaderProviders {
|
||||
if providerAuthHeaderWorks(p) {
|
||||
t.Errorf("got %q as unbroken; want broken", p)
|
||||
}
|
||||
p := fmt.Sprintf("%ssomesuffix", p)
|
||||
if providerAuthHeaderWorks(p) {
|
||||
t.Errorf("got %q as unbroken; want broken", p)
|
||||
}
|
||||
}
|
||||
p := "https://api.not-in-the-list-example.com/"
|
||||
if !providerAuthHeaderWorks(p) {
|
||||
t.Errorf("got %q as unbroken; want broken", p)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProviderAuthHeaderWorksDomain(t *testing.T) {
|
||||
tests := []struct {
|
||||
tokenURL string
|
||||
wantWorks bool
|
||||
}{
|
||||
{"https://dev-12345.okta.com/token-url", false},
|
||||
{"https://dev-12345.oktapreview.com/token-url", false},
|
||||
{"https://dev-12345.okta.org/token-url", true},
|
||||
{"https://foo.bar.force.com/token-url", false},
|
||||
{"https://foo.force.com/token-url", false},
|
||||
{"https://force.com/token-url", true},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
got := providerAuthHeaderWorks(test.tokenURL)
|
||||
if got != test.wantWorks {
|
||||
t.Errorf("providerAuthHeaderWorks(%q) = %v; want %v", test.tokenURL, got, test.wantWorks)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRetrieveTokenWithContexts(t *testing.T) {
|
||||
const clientID = "client-id"
|
||||
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
||||
defer ts.Close()
|
||||
|
||||
_, err := RetrieveToken(context.Background(), clientID, "", ts.URL, url.Values{})
|
||||
if err != nil {
|
||||
t.Errorf("RetrieveToken (with background context) = %v; want no error", err)
|
||||
}
|
||||
|
||||
ctx, cancelfunc := context.WithCancel(context.Background())
|
||||
|
||||
cancellingts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
cancelfunc()
|
||||
}))
|
||||
defer cancellingts.Close()
|
||||
|
||||
_, err = RetrieveToken(ctx, clientID, "", cancellingts.URL, url.Values{})
|
||||
if err == nil {
|
||||
t.Errorf("RetrieveToken (with cancelled context) = nil; want error")
|
||||
}
|
||||
}
|
||||
68
vendor/golang.org/x/oauth2/internal/transport.go
generated
vendored
Normal file
68
vendor/golang.org/x/oauth2/internal/transport.go
generated
vendored
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// HTTPClient is the context key to use with golang.org/x/net/context's
|
||||
// WithValue function to associate an *http.Client value with a context.
|
||||
var HTTPClient ContextKey
|
||||
|
||||
// ContextKey is just an empty struct. It exists so HTTPClient can be
|
||||
// an immutable public variable with a unique type. It's immutable
|
||||
// because nobody else can create a ContextKey, being unexported.
|
||||
type ContextKey struct{}
|
||||
|
||||
// ContextClientFunc is a func which tries to return an *http.Client
|
||||
// given a Context value. If it returns an error, the search stops
|
||||
// with that error. If it returns (nil, nil), the search continues
|
||||
// down the list of registered funcs.
|
||||
type ContextClientFunc func(context.Context) (*http.Client, error)
|
||||
|
||||
var contextClientFuncs []ContextClientFunc
|
||||
|
||||
func RegisterContextClientFunc(fn ContextClientFunc) {
|
||||
contextClientFuncs = append(contextClientFuncs, fn)
|
||||
}
|
||||
|
||||
func ContextClient(ctx context.Context) (*http.Client, error) {
|
||||
if ctx != nil {
|
||||
if hc, ok := ctx.Value(HTTPClient).(*http.Client); ok {
|
||||
return hc, nil
|
||||
}
|
||||
}
|
||||
for _, fn := range contextClientFuncs {
|
||||
c, err := fn(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if c != nil {
|
||||
return c, nil
|
||||
}
|
||||
}
|
||||
return http.DefaultClient, nil
|
||||
}
|
||||
|
||||
func ContextTransport(ctx context.Context) http.RoundTripper {
|
||||
hc, err := ContextClient(ctx)
|
||||
// This is a rare error case (somebody using nil on App Engine).
|
||||
if err != nil {
|
||||
return ErrorTransport{err}
|
||||
}
|
||||
return hc.Transport
|
||||
}
|
||||
|
||||
// ErrorTransport returns the specified error on RoundTrip.
|
||||
// This RoundTripper should be used in rare error cases where
|
||||
// error handling can be postponed to response handling time.
|
||||
type ErrorTransport struct{ Err error }
|
||||
|
||||
func (t ErrorTransport) RoundTrip(*http.Request) (*http.Response, error) {
|
||||
return nil, t.Err
|
||||
}
|
||||
38
vendor/golang.org/x/oauth2/internal/transport_test.go
generated
vendored
Normal file
38
vendor/golang.org/x/oauth2/internal/transport_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
// 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 internal
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func TestContextClient(t *testing.T) {
|
||||
rc := &http.Client{}
|
||||
RegisterContextClientFunc(func(context.Context) (*http.Client, error) {
|
||||
return rc, nil
|
||||
})
|
||||
|
||||
c := &http.Client{}
|
||||
ctx := context.WithValue(context.Background(), HTTPClient, c)
|
||||
|
||||
hc, err := ContextClient(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("want valid client; got err = %v", err)
|
||||
}
|
||||
if hc != c {
|
||||
t.Fatalf("want context client = %p; got = %p", c, hc)
|
||||
}
|
||||
|
||||
hc, err = ContextClient(context.TODO())
|
||||
if err != nil {
|
||||
t.Fatalf("want valid client; got err = %v", err)
|
||||
}
|
||||
if hc != rc {
|
||||
t.Fatalf("want registered client = %p; got = %p", c, hc)
|
||||
}
|
||||
}
|
||||
182
vendor/golang.org/x/oauth2/jws/jws.go
generated
vendored
Normal file
182
vendor/golang.org/x/oauth2/jws/jws.go
generated
vendored
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package jws provides a partial implementation
|
||||
// of JSON Web Signature encoding and decoding.
|
||||
// It exists to support the golang.org/x/oauth2 package.
|
||||
//
|
||||
// See RFC 7515.
|
||||
//
|
||||
// Deprecated: this package is not intended for public use and might be
|
||||
// removed in the future. It exists for internal use only.
|
||||
// Please switch to another JWS package or copy this package into your own
|
||||
// source tree.
|
||||
package jws // import "golang.org/x/oauth2/jws"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ClaimSet contains information about the JWT signature including the
|
||||
// permissions being requested (scopes), the target of the token, the issuer,
|
||||
// the time the token was issued, and the lifetime of the token.
|
||||
type ClaimSet struct {
|
||||
Iss string `json:"iss"` // email address of the client_id of the application making the access token request
|
||||
Scope string `json:"scope,omitempty"` // space-delimited list of the permissions the application requests
|
||||
Aud string `json:"aud"` // descriptor of the intended target of the assertion (Optional).
|
||||
Exp int64 `json:"exp"` // the expiration time of the assertion (seconds since Unix epoch)
|
||||
Iat int64 `json:"iat"` // the time the assertion was issued (seconds since Unix epoch)
|
||||
Typ string `json:"typ,omitempty"` // token type (Optional).
|
||||
|
||||
// Email for which the application is requesting delegated access (Optional).
|
||||
Sub string `json:"sub,omitempty"`
|
||||
|
||||
// The old name of Sub. Client keeps setting Prn to be
|
||||
// complaint with legacy OAuth 2.0 providers. (Optional)
|
||||
Prn string `json:"prn,omitempty"`
|
||||
|
||||
// See http://tools.ietf.org/html/draft-jones-json-web-token-10#section-4.3
|
||||
// This array is marshalled using custom code (see (c *ClaimSet) encode()).
|
||||
PrivateClaims map[string]interface{} `json:"-"`
|
||||
}
|
||||
|
||||
func (c *ClaimSet) encode() (string, error) {
|
||||
// Reverting time back for machines whose time is not perfectly in sync.
|
||||
// If client machine's time is in the future according
|
||||
// to Google servers, an access token will not be issued.
|
||||
now := time.Now().Add(-10 * time.Second)
|
||||
if c.Iat == 0 {
|
||||
c.Iat = now.Unix()
|
||||
}
|
||||
if c.Exp == 0 {
|
||||
c.Exp = now.Add(time.Hour).Unix()
|
||||
}
|
||||
if c.Exp < c.Iat {
|
||||
return "", fmt.Errorf("jws: invalid Exp = %v; must be later than Iat = %v", c.Exp, c.Iat)
|
||||
}
|
||||
|
||||
b, err := json.Marshal(c)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(c.PrivateClaims) == 0 {
|
||||
return base64.RawURLEncoding.EncodeToString(b), nil
|
||||
}
|
||||
|
||||
// Marshal private claim set and then append it to b.
|
||||
prv, err := json.Marshal(c.PrivateClaims)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("jws: invalid map of private claims %v", c.PrivateClaims)
|
||||
}
|
||||
|
||||
// Concatenate public and private claim JSON objects.
|
||||
if !bytes.HasSuffix(b, []byte{'}'}) {
|
||||
return "", fmt.Errorf("jws: invalid JSON %s", b)
|
||||
}
|
||||
if !bytes.HasPrefix(prv, []byte{'{'}) {
|
||||
return "", fmt.Errorf("jws: invalid JSON %s", prv)
|
||||
}
|
||||
b[len(b)-1] = ',' // Replace closing curly brace with a comma.
|
||||
b = append(b, prv[1:]...) // Append private claims.
|
||||
return base64.RawURLEncoding.EncodeToString(b), nil
|
||||
}
|
||||
|
||||
// Header represents the header for the signed JWS payloads.
|
||||
type Header struct {
|
||||
// The algorithm used for signature.
|
||||
Algorithm string `json:"alg"`
|
||||
|
||||
// Represents the token type.
|
||||
Typ string `json:"typ"`
|
||||
|
||||
// The optional hint of which key is being used.
|
||||
KeyID string `json:"kid,omitempty"`
|
||||
}
|
||||
|
||||
func (h *Header) encode() (string, error) {
|
||||
b, err := json.Marshal(h)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.RawURLEncoding.EncodeToString(b), nil
|
||||
}
|
||||
|
||||
// Decode decodes a claim set from a JWS payload.
|
||||
func Decode(payload string) (*ClaimSet, error) {
|
||||
// decode returned id token to get expiry
|
||||
s := strings.Split(payload, ".")
|
||||
if len(s) < 2 {
|
||||
// TODO(jbd): Provide more context about the error.
|
||||
return nil, errors.New("jws: invalid token received")
|
||||
}
|
||||
decoded, err := base64.RawURLEncoding.DecodeString(s[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c := &ClaimSet{}
|
||||
err = json.NewDecoder(bytes.NewBuffer(decoded)).Decode(c)
|
||||
return c, err
|
||||
}
|
||||
|
||||
// Signer returns a signature for the given data.
|
||||
type Signer func(data []byte) (sig []byte, err error)
|
||||
|
||||
// EncodeWithSigner encodes a header and claim set with the provided signer.
|
||||
func EncodeWithSigner(header *Header, c *ClaimSet, sg Signer) (string, error) {
|
||||
head, err := header.encode()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
cs, err := c.encode()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
ss := fmt.Sprintf("%s.%s", head, cs)
|
||||
sig, err := sg([]byte(ss))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("%s.%s", ss, base64.RawURLEncoding.EncodeToString(sig)), nil
|
||||
}
|
||||
|
||||
// Encode encodes a signed JWS with provided header and claim set.
|
||||
// This invokes EncodeWithSigner using crypto/rsa.SignPKCS1v15 with the given RSA private key.
|
||||
func Encode(header *Header, c *ClaimSet, key *rsa.PrivateKey) (string, error) {
|
||||
sg := func(data []byte) (sig []byte, err error) {
|
||||
h := sha256.New()
|
||||
h.Write(data)
|
||||
return rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, h.Sum(nil))
|
||||
}
|
||||
return EncodeWithSigner(header, c, sg)
|
||||
}
|
||||
|
||||
// Verify tests whether the provided JWT token's signature was produced by the private key
|
||||
// associated with the supplied public key.
|
||||
func Verify(token string, key *rsa.PublicKey) error {
|
||||
parts := strings.Split(token, ".")
|
||||
if len(parts) != 3 {
|
||||
return errors.New("jws: invalid token received, token must have 3 parts")
|
||||
}
|
||||
|
||||
signedContent := parts[0] + "." + parts[1]
|
||||
signatureString, err := base64.RawURLEncoding.DecodeString(parts[2])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h := sha256.New()
|
||||
h.Write([]byte(signedContent))
|
||||
return rsa.VerifyPKCS1v15(key, crypto.SHA256, h.Sum(nil), []byte(signatureString))
|
||||
}
|
||||
46
vendor/golang.org/x/oauth2/jws/jws_test.go
generated
vendored
Normal file
46
vendor/golang.org/x/oauth2/jws/jws_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
// 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 jws
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSignAndVerify(t *testing.T) {
|
||||
header := &Header{
|
||||
Algorithm: "RS256",
|
||||
Typ: "JWT",
|
||||
}
|
||||
payload := &ClaimSet{
|
||||
Iss: "http://google.com/",
|
||||
Aud: "",
|
||||
Exp: 3610,
|
||||
Iat: 10,
|
||||
}
|
||||
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
token, err := Encode(header, payload, privateKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = Verify(token, &privateKey.PublicKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyFailsOnMalformedClaim(t *testing.T) {
|
||||
err := Verify("abc.def", nil)
|
||||
if err == nil {
|
||||
t.Error("got no errors; want improperly formed JWT not to be verified")
|
||||
}
|
||||
}
|
||||
33
vendor/golang.org/x/oauth2/jwt/example_test.go
generated
vendored
Normal file
33
vendor/golang.org/x/oauth2/jwt/example_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package jwt_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"golang.org/x/oauth2/jwt"
|
||||
)
|
||||
|
||||
func ExampleJWTConfig() {
|
||||
ctx := context.Background()
|
||||
conf := &jwt.Config{
|
||||
Email: "xxx@developer.com",
|
||||
// The contents of your RSA private key or your PEM file
|
||||
// that contains a private key.
|
||||
// If you have a p12 file instead, you
|
||||
// can use `openssl` to export the private key into a pem file.
|
||||
//
|
||||
// $ openssl pkcs12 -in key.p12 -out key.pem -nodes
|
||||
//
|
||||
// It only supports PEM containers with no passphrase.
|
||||
PrivateKey: []byte("-----BEGIN RSA PRIVATE KEY-----..."),
|
||||
Subject: "user@example.com",
|
||||
TokenURL: "https://provider.com/o/oauth2/token",
|
||||
}
|
||||
// Initiate an http.Client, the following GET request will be
|
||||
// authorized and authenticated on the behalf of user@example.com.
|
||||
client := conf.Client(ctx)
|
||||
client.Get("...")
|
||||
}
|
||||
159
vendor/golang.org/x/oauth2/jwt/jwt.go
generated
vendored
Normal file
159
vendor/golang.org/x/oauth2/jwt/jwt.go
generated
vendored
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package jwt implements the OAuth 2.0 JSON Web Token flow, commonly
|
||||
// known as "two-legged OAuth 2.0".
|
||||
//
|
||||
// See: https://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-12
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/internal"
|
||||
"golang.org/x/oauth2/jws"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultGrantType = "urn:ietf:params:oauth:grant-type:jwt-bearer"
|
||||
defaultHeader = &jws.Header{Algorithm: "RS256", Typ: "JWT"}
|
||||
)
|
||||
|
||||
// Config is the configuration for using JWT to fetch tokens,
|
||||
// commonly known as "two-legged OAuth 2.0".
|
||||
type Config struct {
|
||||
// Email is the OAuth client identifier used when communicating with
|
||||
// the configured OAuth provider.
|
||||
Email string
|
||||
|
||||
// PrivateKey contains the contents of an RSA private key or the
|
||||
// contents of a PEM file that contains a private key. The provided
|
||||
// private key is used to sign JWT payloads.
|
||||
// PEM containers with a passphrase are not supported.
|
||||
// Use the following command to convert a PKCS 12 file into a PEM.
|
||||
//
|
||||
// $ openssl pkcs12 -in key.p12 -out key.pem -nodes
|
||||
//
|
||||
PrivateKey []byte
|
||||
|
||||
// PrivateKeyID contains an optional hint indicating which key is being
|
||||
// used.
|
||||
PrivateKeyID string
|
||||
|
||||
// Subject is the optional user to impersonate.
|
||||
Subject string
|
||||
|
||||
// Scopes optionally specifies a list of requested permission scopes.
|
||||
Scopes []string
|
||||
|
||||
// TokenURL is the endpoint required to complete the 2-legged JWT flow.
|
||||
TokenURL string
|
||||
|
||||
// Expires optionally specifies how long the token is valid for.
|
||||
Expires time.Duration
|
||||
}
|
||||
|
||||
// TokenSource returns a JWT TokenSource using the configuration
|
||||
// in c and the HTTP client from the provided context.
|
||||
func (c *Config) TokenSource(ctx context.Context) oauth2.TokenSource {
|
||||
return oauth2.ReuseTokenSource(nil, jwtSource{ctx, c})
|
||||
}
|
||||
|
||||
// Client returns an HTTP client wrapping the context's
|
||||
// HTTP transport and adding Authorization headers with tokens
|
||||
// obtained from c.
|
||||
//
|
||||
// The returned client and its Transport should not be modified.
|
||||
func (c *Config) Client(ctx context.Context) *http.Client {
|
||||
return oauth2.NewClient(ctx, c.TokenSource(ctx))
|
||||
}
|
||||
|
||||
// jwtSource is a source that always does a signed JWT request for a token.
|
||||
// It should typically be wrapped with a reuseTokenSource.
|
||||
type jwtSource struct {
|
||||
ctx context.Context
|
||||
conf *Config
|
||||
}
|
||||
|
||||
func (js jwtSource) Token() (*oauth2.Token, error) {
|
||||
pk, err := internal.ParseKey(js.conf.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hc := oauth2.NewClient(js.ctx, nil)
|
||||
claimSet := &jws.ClaimSet{
|
||||
Iss: js.conf.Email,
|
||||
Scope: strings.Join(js.conf.Scopes, " "),
|
||||
Aud: js.conf.TokenURL,
|
||||
}
|
||||
if subject := js.conf.Subject; subject != "" {
|
||||
claimSet.Sub = subject
|
||||
// prn is the old name of sub. Keep setting it
|
||||
// to be compatible with legacy OAuth 2.0 providers.
|
||||
claimSet.Prn = subject
|
||||
}
|
||||
if t := js.conf.Expires; t > 0 {
|
||||
claimSet.Exp = time.Now().Add(t).Unix()
|
||||
}
|
||||
h := *defaultHeader
|
||||
h.KeyID = js.conf.PrivateKeyID
|
||||
payload, err := jws.Encode(&h, claimSet, pk)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v := url.Values{}
|
||||
v.Set("grant_type", defaultGrantType)
|
||||
v.Set("assertion", payload)
|
||||
resp, err := hc.PostForm(js.conf.TokenURL, v)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1<<20))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
|
||||
}
|
||||
if c := resp.StatusCode; c < 200 || c > 299 {
|
||||
return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", resp.Status, body)
|
||||
}
|
||||
// tokenRes is the JSON response body.
|
||||
var tokenRes struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
TokenType string `json:"token_type"`
|
||||
IDToken string `json:"id_token"`
|
||||
ExpiresIn int64 `json:"expires_in"` // relative seconds from now
|
||||
}
|
||||
if err := json.Unmarshal(body, &tokenRes); err != nil {
|
||||
return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
|
||||
}
|
||||
token := &oauth2.Token{
|
||||
AccessToken: tokenRes.AccessToken,
|
||||
TokenType: tokenRes.TokenType,
|
||||
}
|
||||
raw := make(map[string]interface{})
|
||||
json.Unmarshal(body, &raw) // no error checks for optional fields
|
||||
token = token.WithExtra(raw)
|
||||
|
||||
if secs := tokenRes.ExpiresIn; secs > 0 {
|
||||
token.Expiry = time.Now().Add(time.Duration(secs) * time.Second)
|
||||
}
|
||||
if v := tokenRes.IDToken; v != "" {
|
||||
// decode returned id token to get expiry
|
||||
claimSet, err := jws.Decode(v)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("oauth2: error decoding JWT token: %v", err)
|
||||
}
|
||||
token.Expiry = time.Unix(claimSet.Exp, 0)
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
190
vendor/golang.org/x/oauth2/jwt/jwt_test.go
generated
vendored
Normal file
190
vendor/golang.org/x/oauth2/jwt/jwt_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/oauth2/jws"
|
||||
)
|
||||
|
||||
var dummyPrivateKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAx4fm7dngEmOULNmAs1IGZ9Apfzh+BkaQ1dzkmbUgpcoghucE
|
||||
DZRnAGd2aPyB6skGMXUytWQvNYav0WTR00wFtX1ohWTfv68HGXJ8QXCpyoSKSSFY
|
||||
fuP9X36wBSkSX9J5DVgiuzD5VBdzUISSmapjKm+DcbRALjz6OUIPEWi1Tjl6p5RK
|
||||
1w41qdbmt7E5/kGhKLDuT7+M83g4VWhgIvaAXtnhklDAggilPPa8ZJ1IFe31lNlr
|
||||
k4DRk38nc6sEutdf3RL7QoH7FBusI7uXV03DC6dwN1kP4GE7bjJhcRb/7jYt7CQ9
|
||||
/E9Exz3c0yAp0yrTg0Fwh+qxfH9dKwN52S7SBwIDAQABAoIBAQCaCs26K07WY5Jt
|
||||
3a2Cw3y2gPrIgTCqX6hJs7O5ByEhXZ8nBwsWANBUe4vrGaajQHdLj5OKfsIDrOvn
|
||||
2NI1MqflqeAbu/kR32q3tq8/Rl+PPiwUsW3E6Pcf1orGMSNCXxeducF2iySySzh3
|
||||
nSIhCG5uwJDWI7a4+9KiieFgK1pt/Iv30q1SQS8IEntTfXYwANQrfKUVMmVF9aIK
|
||||
6/WZE2yd5+q3wVVIJ6jsmTzoDCX6QQkkJICIYwCkglmVy5AeTckOVwcXL0jqw5Kf
|
||||
5/soZJQwLEyBoQq7Kbpa26QHq+CJONetPP8Ssy8MJJXBT+u/bSseMb3Zsr5cr43e
|
||||
DJOhwsThAoGBAPY6rPKl2NT/K7XfRCGm1sbWjUQyDShscwuWJ5+kD0yudnT/ZEJ1
|
||||
M3+KS/iOOAoHDdEDi9crRvMl0UfNa8MAcDKHflzxg2jg/QI+fTBjPP5GOX0lkZ9g
|
||||
z6VePoVoQw2gpPFVNPPTxKfk27tEzbaffvOLGBEih0Kb7HTINkW8rIlzAoGBAM9y
|
||||
1yr+jvfS1cGFtNU+Gotoihw2eMKtIqR03Yn3n0PK1nVCDKqwdUqCypz4+ml6cxRK
|
||||
J8+Pfdh7D+ZJd4LEG6Y4QRDLuv5OA700tUoSHxMSNn3q9As4+T3MUyYxWKvTeu3U
|
||||
f2NWP9ePU0lV8ttk7YlpVRaPQmc1qwooBA/z/8AdAoGAW9x0HWqmRICWTBnpjyxx
|
||||
QGlW9rQ9mHEtUotIaRSJ6K/F3cxSGUEkX1a3FRnp6kPLcckC6NlqdNgNBd6rb2rA
|
||||
cPl/uSkZP42Als+9YMoFPU/xrrDPbUhu72EDrj3Bllnyb168jKLa4VBOccUvggxr
|
||||
Dm08I1hgYgdN5huzs7y6GeUCgYEAj+AZJSOJ6o1aXS6rfV3mMRve9bQ9yt8jcKXw
|
||||
5HhOCEmMtaSKfnOF1Ziih34Sxsb7O2428DiX0mV/YHtBnPsAJidL0SdLWIapBzeg
|
||||
KHArByIRkwE6IvJvwpGMdaex1PIGhx5i/3VZL9qiq/ElT05PhIb+UXgoWMabCp84
|
||||
OgxDK20CgYAeaFo8BdQ7FmVX2+EEejF+8xSge6WVLtkaon8bqcn6P0O8lLypoOhd
|
||||
mJAYH8WU+UAy9pecUnDZj14LAGNVmYcse8HFX71MoshnvCTFEPVo4rZxIAGwMpeJ
|
||||
5jgQ3slYLpqrGlcbLgUXBUgzEO684Wk/UV9DFPlHALVqCfXQ9dpJPg==
|
||||
-----END RSA PRIVATE KEY-----`)
|
||||
|
||||
func TestJWTFetch_JSONResponse(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write([]byte(`{
|
||||
"access_token": "90d64460d14870c08c81352a05dedd3465940a7c",
|
||||
"scope": "user",
|
||||
"token_type": "bearer",
|
||||
"expires_in": 3600
|
||||
}`))
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
conf := &Config{
|
||||
Email: "aaa@xxx.com",
|
||||
PrivateKey: dummyPrivateKey,
|
||||
TokenURL: ts.URL,
|
||||
}
|
||||
tok, err := conf.TokenSource(context.Background()).Token()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !tok.Valid() {
|
||||
t.Errorf("got invalid token: %v", tok)
|
||||
}
|
||||
if got, want := tok.AccessToken, "90d64460d14870c08c81352a05dedd3465940a7c"; got != want {
|
||||
t.Errorf("access token = %q; want %q", got, want)
|
||||
}
|
||||
if got, want := tok.TokenType, "bearer"; got != want {
|
||||
t.Errorf("token type = %q; want %q", got, want)
|
||||
}
|
||||
if got := tok.Expiry.IsZero(); got {
|
||||
t.Errorf("token expiry = %v, want none", got)
|
||||
}
|
||||
scope := tok.Extra("scope")
|
||||
if got, want := scope, "user"; got != want {
|
||||
t.Errorf("scope = %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJWTFetch_BadResponse(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write([]byte(`{"scope": "user", "token_type": "bearer"}`))
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
conf := &Config{
|
||||
Email: "aaa@xxx.com",
|
||||
PrivateKey: dummyPrivateKey,
|
||||
TokenURL: ts.URL,
|
||||
}
|
||||
tok, err := conf.TokenSource(context.Background()).Token()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if tok == nil {
|
||||
t.Fatalf("got nil token; want token")
|
||||
}
|
||||
if tok.Valid() {
|
||||
t.Errorf("got invalid token: %v", tok)
|
||||
}
|
||||
if got, want := tok.AccessToken, ""; got != want {
|
||||
t.Errorf("access token = %q; want %q", got, want)
|
||||
}
|
||||
if got, want := tok.TokenType, "bearer"; got != want {
|
||||
t.Errorf("token type = %q; want %q", got, want)
|
||||
}
|
||||
scope := tok.Extra("scope")
|
||||
if got, want := scope, "user"; got != want {
|
||||
t.Errorf("token scope = %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJWTFetch_BadResponseType(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write([]byte(`{"access_token":123, "scope": "user", "token_type": "bearer"}`))
|
||||
}))
|
||||
defer ts.Close()
|
||||
conf := &Config{
|
||||
Email: "aaa@xxx.com",
|
||||
PrivateKey: dummyPrivateKey,
|
||||
TokenURL: ts.URL,
|
||||
}
|
||||
tok, err := conf.TokenSource(context.Background()).Token()
|
||||
if err == nil {
|
||||
t.Error("got a token; expected error")
|
||||
if got, want := tok.AccessToken, ""; got != want {
|
||||
t.Errorf("access token = %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestJWTFetch_Assertion(t *testing.T) {
|
||||
var assertion string
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
assertion = r.Form.Get("assertion")
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write([]byte(`{
|
||||
"access_token": "90d64460d14870c08c81352a05dedd3465940a7c",
|
||||
"scope": "user",
|
||||
"token_type": "bearer",
|
||||
"expires_in": 3600
|
||||
}`))
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
conf := &Config{
|
||||
Email: "aaa@xxx.com",
|
||||
PrivateKey: dummyPrivateKey,
|
||||
PrivateKeyID: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
||||
TokenURL: ts.URL,
|
||||
}
|
||||
|
||||
_, err := conf.TokenSource(context.Background()).Token()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to fetch token: %v", err)
|
||||
}
|
||||
|
||||
parts := strings.Split(assertion, ".")
|
||||
if len(parts) != 3 {
|
||||
t.Fatalf("assertion = %q; want 3 parts", assertion)
|
||||
}
|
||||
gotjson, err := base64.RawURLEncoding.DecodeString(parts[0])
|
||||
if err != nil {
|
||||
t.Fatalf("invalid token header; err = %v", err)
|
||||
}
|
||||
|
||||
got := jws.Header{}
|
||||
if err := json.Unmarshal(gotjson, &got); err != nil {
|
||||
t.Errorf("failed to unmarshal json token header = %q; err = %v", gotjson, err)
|
||||
}
|
||||
|
||||
want := jws.Header{
|
||||
Algorithm: "RS256",
|
||||
Typ: "JWT",
|
||||
KeyID: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
||||
}
|
||||
if got != want {
|
||||
t.Errorf("access token header = %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
344
vendor/golang.org/x/oauth2/oauth2.go
generated
vendored
Normal file
344
vendor/golang.org/x/oauth2/oauth2.go
generated
vendored
Normal file
|
|
@ -0,0 +1,344 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package oauth2 provides support for making
|
||||
// OAuth2 authorized and authenticated HTTP requests.
|
||||
// It can additionally grant authorization with Bearer JWT.
|
||||
package oauth2 // import "golang.org/x/oauth2"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/oauth2/internal"
|
||||
)
|
||||
|
||||
// NoContext is the default context you should supply if not using
|
||||
// your own context.Context (see https://golang.org/x/net/context).
|
||||
//
|
||||
// Deprecated: Use context.Background() or context.TODO() instead.
|
||||
var NoContext = context.TODO()
|
||||
|
||||
// RegisterBrokenAuthHeaderProvider registers an OAuth2 server
|
||||
// identified by the tokenURL prefix as an OAuth2 implementation
|
||||
// which doesn't support the HTTP Basic authentication
|
||||
// scheme to authenticate with the authorization server.
|
||||
// Once a server is registered, credentials (client_id and client_secret)
|
||||
// will be passed as query parameters rather than being present
|
||||
// in the Authorization header.
|
||||
// See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
|
||||
func RegisterBrokenAuthHeaderProvider(tokenURL string) {
|
||||
internal.RegisterBrokenAuthHeaderProvider(tokenURL)
|
||||
}
|
||||
|
||||
// Config describes a typical 3-legged OAuth2 flow, with both the
|
||||
// client application information and the server's endpoint URLs.
|
||||
// For the client credentials 2-legged OAuth2 flow, see the clientcredentials
|
||||
// package (https://golang.org/x/oauth2/clientcredentials).
|
||||
type Config struct {
|
||||
// ClientID is the application's ID.
|
||||
ClientID string
|
||||
|
||||
// ClientSecret is the application's secret.
|
||||
ClientSecret string
|
||||
|
||||
// Endpoint contains the resource server's token endpoint
|
||||
// URLs. These are constants specific to each server and are
|
||||
// often available via site-specific packages, such as
|
||||
// google.Endpoint or github.Endpoint.
|
||||
Endpoint Endpoint
|
||||
|
||||
// RedirectURL is the URL to redirect users going through
|
||||
// the OAuth flow, after the resource owner's URLs.
|
||||
RedirectURL string
|
||||
|
||||
// Scope specifies optional requested permissions.
|
||||
Scopes []string
|
||||
}
|
||||
|
||||
// A TokenSource is anything that can return a token.
|
||||
type TokenSource interface {
|
||||
// Token returns a token or an error.
|
||||
// Token must be safe for concurrent use by multiple goroutines.
|
||||
// The returned Token must not be modified.
|
||||
Token() (*Token, error)
|
||||
}
|
||||
|
||||
// Endpoint contains the OAuth 2.0 provider's authorization and token
|
||||
// endpoint URLs.
|
||||
type Endpoint struct {
|
||||
AuthURL string
|
||||
TokenURL string
|
||||
}
|
||||
|
||||
var (
|
||||
// AccessTypeOnline and AccessTypeOffline are options passed
|
||||
// to the Options.AuthCodeURL method. They modify the
|
||||
// "access_type" field that gets sent in the URL returned by
|
||||
// AuthCodeURL.
|
||||
//
|
||||
// Online is the default if neither is specified. If your
|
||||
// application needs to refresh access tokens when the user
|
||||
// is not present at the browser, then use offline. This will
|
||||
// result in your application obtaining a refresh token the
|
||||
// first time your application exchanges an authorization
|
||||
// code for a user.
|
||||
AccessTypeOnline AuthCodeOption = SetAuthURLParam("access_type", "online")
|
||||
AccessTypeOffline AuthCodeOption = SetAuthURLParam("access_type", "offline")
|
||||
|
||||
// ApprovalForce forces the users to view the consent dialog
|
||||
// and confirm the permissions request at the URL returned
|
||||
// from AuthCodeURL, even if they've already done so.
|
||||
ApprovalForce AuthCodeOption = SetAuthURLParam("approval_prompt", "force")
|
||||
)
|
||||
|
||||
// An AuthCodeOption is passed to Config.AuthCodeURL.
|
||||
type AuthCodeOption interface {
|
||||
setValue(url.Values)
|
||||
}
|
||||
|
||||
type setParam struct{ k, v string }
|
||||
|
||||
func (p setParam) setValue(m url.Values) { m.Set(p.k, p.v) }
|
||||
|
||||
// SetAuthURLParam builds an AuthCodeOption which passes key/value parameters
|
||||
// to a provider's authorization endpoint.
|
||||
func SetAuthURLParam(key, value string) AuthCodeOption {
|
||||
return setParam{key, value}
|
||||
}
|
||||
|
||||
// AuthCodeURL returns a URL to OAuth 2.0 provider's consent page
|
||||
// that asks for permissions for the required scopes explicitly.
|
||||
//
|
||||
// State is a token to protect the user from CSRF attacks. You must
|
||||
// always provide a non-zero string and validate that it matches the
|
||||
// the state query parameter on your redirect callback.
|
||||
// See http://tools.ietf.org/html/rfc6749#section-10.12 for more info.
|
||||
//
|
||||
// Opts may include AccessTypeOnline or AccessTypeOffline, as well
|
||||
// as ApprovalForce.
|
||||
func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(c.Endpoint.AuthURL)
|
||||
v := url.Values{
|
||||
"response_type": {"code"},
|
||||
"client_id": {c.ClientID},
|
||||
"redirect_uri": internal.CondVal(c.RedirectURL),
|
||||
"scope": internal.CondVal(strings.Join(c.Scopes, " ")),
|
||||
"state": internal.CondVal(state),
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt.setValue(v)
|
||||
}
|
||||
if strings.Contains(c.Endpoint.AuthURL, "?") {
|
||||
buf.WriteByte('&')
|
||||
} else {
|
||||
buf.WriteByte('?')
|
||||
}
|
||||
buf.WriteString(v.Encode())
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// PasswordCredentialsToken converts a resource owner username and password
|
||||
// pair into a token.
|
||||
//
|
||||
// Per the RFC, this grant type should only be used "when there is a high
|
||||
// degree of trust between the resource owner and the client (e.g., the client
|
||||
// is part of the device operating system or a highly privileged application),
|
||||
// and when other authorization grant types are not available."
|
||||
// See https://tools.ietf.org/html/rfc6749#section-4.3 for more info.
|
||||
//
|
||||
// The HTTP client to use is derived from the context.
|
||||
// If nil, http.DefaultClient is used.
|
||||
func (c *Config) PasswordCredentialsToken(ctx context.Context, username, password string) (*Token, error) {
|
||||
return retrieveToken(ctx, c, url.Values{
|
||||
"grant_type": {"password"},
|
||||
"username": {username},
|
||||
"password": {password},
|
||||
"scope": internal.CondVal(strings.Join(c.Scopes, " ")),
|
||||
})
|
||||
}
|
||||
|
||||
// Exchange converts an authorization code into a token.
|
||||
//
|
||||
// It is used after a resource provider redirects the user back
|
||||
// to the Redirect URI (the URL obtained from AuthCodeURL).
|
||||
//
|
||||
// The HTTP client to use is derived from the context.
|
||||
// If a client is not provided via the context, http.DefaultClient is used.
|
||||
//
|
||||
// The code will be in the *http.Request.FormValue("code"). Before
|
||||
// calling Exchange, be sure to validate FormValue("state").
|
||||
func (c *Config) Exchange(ctx context.Context, code string) (*Token, error) {
|
||||
return retrieveToken(ctx, c, url.Values{
|
||||
"grant_type": {"authorization_code"},
|
||||
"code": {code},
|
||||
"redirect_uri": internal.CondVal(c.RedirectURL),
|
||||
})
|
||||
}
|
||||
|
||||
// Client returns an HTTP client using the provided token.
|
||||
// The token will auto-refresh as necessary. The underlying
|
||||
// HTTP transport will be obtained using the provided context.
|
||||
// The returned client and its Transport should not be modified.
|
||||
func (c *Config) Client(ctx context.Context, t *Token) *http.Client {
|
||||
return NewClient(ctx, c.TokenSource(ctx, t))
|
||||
}
|
||||
|
||||
// TokenSource returns a TokenSource that returns t until t expires,
|
||||
// automatically refreshing it as necessary using the provided context.
|
||||
//
|
||||
// Most users will use Config.Client instead.
|
||||
func (c *Config) TokenSource(ctx context.Context, t *Token) TokenSource {
|
||||
tkr := &tokenRefresher{
|
||||
ctx: ctx,
|
||||
conf: c,
|
||||
}
|
||||
if t != nil {
|
||||
tkr.refreshToken = t.RefreshToken
|
||||
}
|
||||
return &reuseTokenSource{
|
||||
t: t,
|
||||
new: tkr,
|
||||
}
|
||||
}
|
||||
|
||||
// tokenRefresher is a TokenSource that makes "grant_type"=="refresh_token"
|
||||
// HTTP requests to renew a token using a RefreshToken.
|
||||
type tokenRefresher struct {
|
||||
ctx context.Context // used to get HTTP requests
|
||||
conf *Config
|
||||
refreshToken string
|
||||
}
|
||||
|
||||
// WARNING: Token is not safe for concurrent access, as it
|
||||
// updates the tokenRefresher's refreshToken field.
|
||||
// Within this package, it is used by reuseTokenSource which
|
||||
// synchronizes calls to this method with its own mutex.
|
||||
func (tf *tokenRefresher) Token() (*Token, error) {
|
||||
if tf.refreshToken == "" {
|
||||
return nil, errors.New("oauth2: token expired and refresh token is not set")
|
||||
}
|
||||
|
||||
tk, err := retrieveToken(tf.ctx, tf.conf, url.Values{
|
||||
"grant_type": {"refresh_token"},
|
||||
"refresh_token": {tf.refreshToken},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tf.refreshToken != tk.RefreshToken {
|
||||
tf.refreshToken = tk.RefreshToken
|
||||
}
|
||||
return tk, err
|
||||
}
|
||||
|
||||
// reuseTokenSource is a TokenSource that holds a single token in memory
|
||||
// and validates its expiry before each call to retrieve it with
|
||||
// Token. If it's expired, it will be auto-refreshed using the
|
||||
// new TokenSource.
|
||||
type reuseTokenSource struct {
|
||||
new TokenSource // called when t is expired.
|
||||
|
||||
mu sync.Mutex // guards t
|
||||
t *Token
|
||||
}
|
||||
|
||||
// Token returns the current token if it's still valid, else will
|
||||
// refresh the current token (using r.Context for HTTP client
|
||||
// information) and return the new one.
|
||||
func (s *reuseTokenSource) Token() (*Token, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if s.t.Valid() {
|
||||
return s.t, nil
|
||||
}
|
||||
t, err := s.new.Token()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.t = t
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// StaticTokenSource returns a TokenSource that always returns the same token.
|
||||
// Because the provided token t is never refreshed, StaticTokenSource is only
|
||||
// useful for tokens that never expire.
|
||||
func StaticTokenSource(t *Token) TokenSource {
|
||||
return staticTokenSource{t}
|
||||
}
|
||||
|
||||
// staticTokenSource is a TokenSource that always returns the same Token.
|
||||
type staticTokenSource struct {
|
||||
t *Token
|
||||
}
|
||||
|
||||
func (s staticTokenSource) Token() (*Token, error) {
|
||||
return s.t, nil
|
||||
}
|
||||
|
||||
// HTTPClient is the context key to use with golang.org/x/net/context's
|
||||
// WithValue function to associate an *http.Client value with a context.
|
||||
var HTTPClient internal.ContextKey
|
||||
|
||||
// NewClient creates an *http.Client from a Context and TokenSource.
|
||||
// The returned client is not valid beyond the lifetime of the context.
|
||||
//
|
||||
// Note that if a custom *http.Client is provided via the Context it
|
||||
// is used only for token acquisition and is not used to configure the
|
||||
// *http.Client returned from NewClient.
|
||||
//
|
||||
// As a special case, if src is nil, a non-OAuth2 client is returned
|
||||
// using the provided context. This exists to support related OAuth2
|
||||
// packages.
|
||||
func NewClient(ctx context.Context, src TokenSource) *http.Client {
|
||||
if src == nil {
|
||||
c, err := internal.ContextClient(ctx)
|
||||
if err != nil {
|
||||
return &http.Client{Transport: internal.ErrorTransport{Err: err}}
|
||||
}
|
||||
return c
|
||||
}
|
||||
return &http.Client{
|
||||
Transport: &Transport{
|
||||
Base: internal.ContextTransport(ctx),
|
||||
Source: ReuseTokenSource(nil, src),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ReuseTokenSource returns a TokenSource which repeatedly returns the
|
||||
// same token as long as it's valid, starting with t.
|
||||
// When its cached token is invalid, a new token is obtained from src.
|
||||
//
|
||||
// ReuseTokenSource is typically used to reuse tokens from a cache
|
||||
// (such as a file on disk) between runs of a program, rather than
|
||||
// obtaining new tokens unnecessarily.
|
||||
//
|
||||
// The initial token t may be nil, in which case the TokenSource is
|
||||
// wrapped in a caching version if it isn't one already. This also
|
||||
// means it's always safe to wrap ReuseTokenSource around any other
|
||||
// TokenSource without adverse effects.
|
||||
func ReuseTokenSource(t *Token, src TokenSource) TokenSource {
|
||||
// Don't wrap a reuseTokenSource in itself. That would work,
|
||||
// but cause an unnecessary number of mutex operations.
|
||||
// Just build the equivalent one.
|
||||
if rt, ok := src.(*reuseTokenSource); ok {
|
||||
if t == nil {
|
||||
// Just use it directly.
|
||||
return rt
|
||||
}
|
||||
src = rt.new
|
||||
}
|
||||
return &reuseTokenSource{
|
||||
t: t,
|
||||
new: src,
|
||||
}
|
||||
}
|
||||
490
vendor/golang.org/x/oauth2/oauth2_test.go
generated
vendored
Normal file
490
vendor/golang.org/x/oauth2/oauth2_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,490 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package oauth2
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type mockTransport struct {
|
||||
rt func(req *http.Request) (resp *http.Response, err error)
|
||||
}
|
||||
|
||||
func (t *mockTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
|
||||
return t.rt(req)
|
||||
}
|
||||
|
||||
func newConf(url string) *Config {
|
||||
return &Config{
|
||||
ClientID: "CLIENT_ID",
|
||||
ClientSecret: "CLIENT_SECRET",
|
||||
RedirectURL: "REDIRECT_URL",
|
||||
Scopes: []string{"scope1", "scope2"},
|
||||
Endpoint: Endpoint{
|
||||
AuthURL: url + "/auth",
|
||||
TokenURL: url + "/token",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthCodeURL(t *testing.T) {
|
||||
conf := newConf("server")
|
||||
url := conf.AuthCodeURL("foo", AccessTypeOffline, ApprovalForce)
|
||||
const want = "server/auth?access_type=offline&approval_prompt=force&client_id=CLIENT_ID&redirect_uri=REDIRECT_URL&response_type=code&scope=scope1+scope2&state=foo"
|
||||
if got := url; got != want {
|
||||
t.Errorf("got auth code URL = %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthCodeURL_CustomParam(t *testing.T) {
|
||||
conf := newConf("server")
|
||||
param := SetAuthURLParam("foo", "bar")
|
||||
url := conf.AuthCodeURL("baz", param)
|
||||
const want = "server/auth?client_id=CLIENT_ID&foo=bar&redirect_uri=REDIRECT_URL&response_type=code&scope=scope1+scope2&state=baz"
|
||||
if got := url; got != want {
|
||||
t.Errorf("got auth code = %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthCodeURL_Optional(t *testing.T) {
|
||||
conf := &Config{
|
||||
ClientID: "CLIENT_ID",
|
||||
Endpoint: Endpoint{
|
||||
AuthURL: "/auth-url",
|
||||
TokenURL: "/token-url",
|
||||
},
|
||||
}
|
||||
url := conf.AuthCodeURL("")
|
||||
const want = "/auth-url?client_id=CLIENT_ID&response_type=code"
|
||||
if got := url; got != want {
|
||||
t.Fatalf("got auth code = %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestURLUnsafeClientConfig(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("Authorization"), "Basic Q0xJRU5UX0lEJTNGJTNGOkNMSUVOVF9TRUNSRVQlM0YlM0Y="; got != want {
|
||||
t.Errorf("Authorization header = %q; want %q", got, want)
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
w.Write([]byte("access_token=90d64460d14870c08c81352a05dedd3465940a7c&scope=user&token_type=bearer"))
|
||||
}))
|
||||
defer ts.Close()
|
||||
conf := newConf(ts.URL)
|
||||
conf.ClientID = "CLIENT_ID??"
|
||||
conf.ClientSecret = "CLIENT_SECRET??"
|
||||
_, err := conf.Exchange(context.Background(), "exchange-code")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExchangeRequest(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.String() != "/token" {
|
||||
t.Errorf("Unexpected exchange request URL, %v is found.", r.URL)
|
||||
}
|
||||
headerAuth := r.Header.Get("Authorization")
|
||||
if headerAuth != "Basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ=" {
|
||||
t.Errorf("Unexpected authorization header, %v is found.", headerAuth)
|
||||
}
|
||||
headerContentType := r.Header.Get("Content-Type")
|
||||
if headerContentType != "application/x-www-form-urlencoded" {
|
||||
t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType)
|
||||
}
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
t.Errorf("Failed reading request body: %s.", err)
|
||||
}
|
||||
if string(body) != "code=exchange-code&grant_type=authorization_code&redirect_uri=REDIRECT_URL" {
|
||||
t.Errorf("Unexpected exchange payload, %v is found.", string(body))
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
w.Write([]byte("access_token=90d64460d14870c08c81352a05dedd3465940a7c&scope=user&token_type=bearer"))
|
||||
}))
|
||||
defer ts.Close()
|
||||
conf := newConf(ts.URL)
|
||||
tok, err := conf.Exchange(context.Background(), "exchange-code")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if !tok.Valid() {
|
||||
t.Fatalf("Token invalid. Got: %#v", tok)
|
||||
}
|
||||
if tok.AccessToken != "90d64460d14870c08c81352a05dedd3465940a7c" {
|
||||
t.Errorf("Unexpected access token, %#v.", tok.AccessToken)
|
||||
}
|
||||
if tok.TokenType != "bearer" {
|
||||
t.Errorf("Unexpected token type, %#v.", tok.TokenType)
|
||||
}
|
||||
scope := tok.Extra("scope")
|
||||
if scope != "user" {
|
||||
t.Errorf("Unexpected value for scope: %v", scope)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExchangeRequest_JSONResponse(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.String() != "/token" {
|
||||
t.Errorf("Unexpected exchange request URL, %v is found.", r.URL)
|
||||
}
|
||||
headerAuth := r.Header.Get("Authorization")
|
||||
if headerAuth != "Basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ=" {
|
||||
t.Errorf("Unexpected authorization header, %v is found.", headerAuth)
|
||||
}
|
||||
headerContentType := r.Header.Get("Content-Type")
|
||||
if headerContentType != "application/x-www-form-urlencoded" {
|
||||
t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType)
|
||||
}
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
t.Errorf("Failed reading request body: %s.", err)
|
||||
}
|
||||
if string(body) != "code=exchange-code&grant_type=authorization_code&redirect_uri=REDIRECT_URL" {
|
||||
t.Errorf("Unexpected exchange payload, %v is found.", string(body))
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write([]byte(`{"access_token": "90d64460d14870c08c81352a05dedd3465940a7c", "scope": "user", "token_type": "bearer", "expires_in": 86400}`))
|
||||
}))
|
||||
defer ts.Close()
|
||||
conf := newConf(ts.URL)
|
||||
tok, err := conf.Exchange(context.Background(), "exchange-code")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if !tok.Valid() {
|
||||
t.Fatalf("Token invalid. Got: %#v", tok)
|
||||
}
|
||||
if tok.AccessToken != "90d64460d14870c08c81352a05dedd3465940a7c" {
|
||||
t.Errorf("Unexpected access token, %#v.", tok.AccessToken)
|
||||
}
|
||||
if tok.TokenType != "bearer" {
|
||||
t.Errorf("Unexpected token type, %#v.", tok.TokenType)
|
||||
}
|
||||
scope := tok.Extra("scope")
|
||||
if scope != "user" {
|
||||
t.Errorf("Unexpected value for scope: %v", scope)
|
||||
}
|
||||
expiresIn := tok.Extra("expires_in")
|
||||
if expiresIn != float64(86400) {
|
||||
t.Errorf("Unexpected non-numeric value for expires_in: %v", expiresIn)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtraValueRetrieval(t *testing.T) {
|
||||
values := url.Values{}
|
||||
kvmap := map[string]string{
|
||||
"scope": "user", "token_type": "bearer", "expires_in": "86400.92",
|
||||
"server_time": "1443571905.5606415", "referer_ip": "10.0.0.1",
|
||||
"etag": "\"afZYj912P4alikMz_P11982\"", "request_id": "86400",
|
||||
"untrimmed": " untrimmed ",
|
||||
}
|
||||
for key, value := range kvmap {
|
||||
values.Set(key, value)
|
||||
}
|
||||
|
||||
tok := Token{raw: values}
|
||||
scope := tok.Extra("scope")
|
||||
if got, want := scope, "user"; got != want {
|
||||
t.Errorf("got scope = %q; want %q", got, want)
|
||||
}
|
||||
serverTime := tok.Extra("server_time")
|
||||
if got, want := serverTime, 1443571905.5606415; got != want {
|
||||
t.Errorf("got server_time value = %v; want %v", got, want)
|
||||
}
|
||||
refererIP := tok.Extra("referer_ip")
|
||||
if got, want := refererIP, "10.0.0.1"; got != want {
|
||||
t.Errorf("got referer_ip value = %v, want %v", got, want)
|
||||
}
|
||||
expiresIn := tok.Extra("expires_in")
|
||||
if got, want := expiresIn, 86400.92; got != want {
|
||||
t.Errorf("got expires_in value = %v, want %v", got, want)
|
||||
}
|
||||
requestID := tok.Extra("request_id")
|
||||
if got, want := requestID, int64(86400); got != want {
|
||||
t.Errorf("got request_id value = %v, want %v", got, want)
|
||||
}
|
||||
untrimmed := tok.Extra("untrimmed")
|
||||
if got, want := untrimmed, " untrimmed "; got != want {
|
||||
t.Errorf("got untrimmed = %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
const day = 24 * time.Hour
|
||||
|
||||
func TestExchangeRequest_JSONResponse_Expiry(t *testing.T) {
|
||||
seconds := int32(day.Seconds())
|
||||
for _, c := range []struct {
|
||||
expires string
|
||||
want bool
|
||||
}{
|
||||
{fmt.Sprintf(`"expires_in": %d`, seconds), true},
|
||||
{fmt.Sprintf(`"expires_in": "%d"`, seconds), true}, // PayPal case
|
||||
{fmt.Sprintf(`"expires": %d`, seconds), true}, // Facebook case
|
||||
{`"expires": false`, false}, // wrong type
|
||||
{`"expires": {}`, false}, // wrong type
|
||||
{`"expires": "zzz"`, false}, // wrong value
|
||||
} {
|
||||
testExchangeRequest_JSONResponse_expiry(t, c.expires, c.want)
|
||||
}
|
||||
}
|
||||
|
||||
func testExchangeRequest_JSONResponse_expiry(t *testing.T, exp string, want bool) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write([]byte(fmt.Sprintf(`{"access_token": "90d", "scope": "user", "token_type": "bearer", %s}`, exp)))
|
||||
}))
|
||||
defer ts.Close()
|
||||
conf := newConf(ts.URL)
|
||||
t1 := time.Now().Add(day)
|
||||
tok, err := conf.Exchange(context.Background(), "exchange-code")
|
||||
t2 := time.Now().Add(day)
|
||||
|
||||
if got := (err == nil); got != want {
|
||||
if want {
|
||||
t.Errorf("unexpected error: got %v", err)
|
||||
} else {
|
||||
t.Errorf("unexpected success")
|
||||
}
|
||||
}
|
||||
if !want {
|
||||
return
|
||||
}
|
||||
if !tok.Valid() {
|
||||
t.Fatalf("Token invalid. Got: %#v", tok)
|
||||
}
|
||||
expiry := tok.Expiry
|
||||
if expiry.Before(t1) || expiry.After(t2) {
|
||||
t.Errorf("Unexpected value for Expiry: %v (shold be between %v and %v)", expiry, t1, t2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExchangeRequest_BadResponse(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write([]byte(`{"scope": "user", "token_type": "bearer"}`))
|
||||
}))
|
||||
defer ts.Close()
|
||||
conf := newConf(ts.URL)
|
||||
tok, err := conf.Exchange(context.Background(), "code")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if tok.AccessToken != "" {
|
||||
t.Errorf("Unexpected access token, %#v.", tok.AccessToken)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExchangeRequest_BadResponseType(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write([]byte(`{"access_token":123, "scope": "user", "token_type": "bearer"}`))
|
||||
}))
|
||||
defer ts.Close()
|
||||
conf := newConf(ts.URL)
|
||||
_, err := conf.Exchange(context.Background(), "exchange-code")
|
||||
if err == nil {
|
||||
t.Error("expected error from invalid access_token type")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExchangeRequest_NonBasicAuth(t *testing.T) {
|
||||
tr := &mockTransport{
|
||||
rt: func(r *http.Request) (w *http.Response, err error) {
|
||||
headerAuth := r.Header.Get("Authorization")
|
||||
if headerAuth != "" {
|
||||
t.Errorf("Unexpected authorization header, %v is found.", headerAuth)
|
||||
}
|
||||
return nil, errors.New("no response")
|
||||
},
|
||||
}
|
||||
c := &http.Client{Transport: tr}
|
||||
conf := &Config{
|
||||
ClientID: "CLIENT_ID",
|
||||
Endpoint: Endpoint{
|
||||
AuthURL: "https://accounts.google.com/auth",
|
||||
TokenURL: "https://accounts.google.com/token",
|
||||
},
|
||||
}
|
||||
|
||||
ctx := context.WithValue(context.Background(), HTTPClient, c)
|
||||
conf.Exchange(ctx, "code")
|
||||
}
|
||||
|
||||
func TestPasswordCredentialsTokenRequest(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
expected := "/token"
|
||||
if r.URL.String() != expected {
|
||||
t.Errorf("URL = %q; want %q", r.URL, expected)
|
||||
}
|
||||
headerAuth := r.Header.Get("Authorization")
|
||||
expected = "Basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ="
|
||||
if headerAuth != expected {
|
||||
t.Errorf("Authorization header = %q; want %q", headerAuth, expected)
|
||||
}
|
||||
headerContentType := r.Header.Get("Content-Type")
|
||||
expected = "application/x-www-form-urlencoded"
|
||||
if headerContentType != expected {
|
||||
t.Errorf("Content-Type header = %q; want %q", headerContentType, expected)
|
||||
}
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
t.Errorf("Failed reading request body: %s.", err)
|
||||
}
|
||||
expected = "grant_type=password&password=password1&scope=scope1+scope2&username=user1"
|
||||
if string(body) != expected {
|
||||
t.Errorf("res.Body = %q; want %q", string(body), expected)
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
w.Write([]byte("access_token=90d64460d14870c08c81352a05dedd3465940a7c&scope=user&token_type=bearer"))
|
||||
}))
|
||||
defer ts.Close()
|
||||
conf := newConf(ts.URL)
|
||||
tok, err := conf.PasswordCredentialsToken(context.Background(), "user1", "password1")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if !tok.Valid() {
|
||||
t.Fatalf("Token invalid. Got: %#v", tok)
|
||||
}
|
||||
expected := "90d64460d14870c08c81352a05dedd3465940a7c"
|
||||
if tok.AccessToken != expected {
|
||||
t.Errorf("AccessToken = %q; want %q", tok.AccessToken, expected)
|
||||
}
|
||||
expected = "bearer"
|
||||
if tok.TokenType != expected {
|
||||
t.Errorf("TokenType = %q; want %q", tok.TokenType, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenRefreshRequest(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.String() == "/somethingelse" {
|
||||
return
|
||||
}
|
||||
if r.URL.String() != "/token" {
|
||||
t.Errorf("Unexpected token refresh request URL, %v is found.", r.URL)
|
||||
}
|
||||
headerContentType := r.Header.Get("Content-Type")
|
||||
if headerContentType != "application/x-www-form-urlencoded" {
|
||||
t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType)
|
||||
}
|
||||
body, _ := ioutil.ReadAll(r.Body)
|
||||
if string(body) != "grant_type=refresh_token&refresh_token=REFRESH_TOKEN" {
|
||||
t.Errorf("Unexpected refresh token payload, %v is found.", string(body))
|
||||
}
|
||||
}))
|
||||
defer ts.Close()
|
||||
conf := newConf(ts.URL)
|
||||
c := conf.Client(context.Background(), &Token{RefreshToken: "REFRESH_TOKEN"})
|
||||
c.Get(ts.URL + "/somethingelse")
|
||||
}
|
||||
|
||||
func TestFetchWithNoRefreshToken(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.String() == "/somethingelse" {
|
||||
return
|
||||
}
|
||||
if r.URL.String() != "/token" {
|
||||
t.Errorf("Unexpected token refresh request URL, %v is found.", r.URL)
|
||||
}
|
||||
headerContentType := r.Header.Get("Content-Type")
|
||||
if headerContentType != "application/x-www-form-urlencoded" {
|
||||
t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType)
|
||||
}
|
||||
body, _ := ioutil.ReadAll(r.Body)
|
||||
if string(body) != "client_id=CLIENT_ID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN" {
|
||||
t.Errorf("Unexpected refresh token payload, %v is found.", string(body))
|
||||
}
|
||||
}))
|
||||
defer ts.Close()
|
||||
conf := newConf(ts.URL)
|
||||
c := conf.Client(context.Background(), nil)
|
||||
_, err := c.Get(ts.URL + "/somethingelse")
|
||||
if err == nil {
|
||||
t.Errorf("Fetch should return an error if no refresh token is set")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRefreshToken_RefreshTokenReplacement(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write([]byte(`{"access_token":"ACCESS_TOKEN", "scope": "user", "token_type": "bearer", "refresh_token": "NEW_REFRESH_TOKEN"}`))
|
||||
return
|
||||
}))
|
||||
defer ts.Close()
|
||||
conf := newConf(ts.URL)
|
||||
tkr := &tokenRefresher{
|
||||
conf: conf,
|
||||
ctx: context.Background(),
|
||||
refreshToken: "OLD_REFRESH_TOKEN",
|
||||
}
|
||||
tk, err := tkr.Token()
|
||||
if err != nil {
|
||||
t.Errorf("got err = %v; want none", err)
|
||||
return
|
||||
}
|
||||
if tk.RefreshToken != tkr.refreshToken {
|
||||
t.Errorf("tokenRefresher.refresh_token = %q; want %q", tkr.refreshToken, tk.RefreshToken)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRefreshToken_RefreshTokenPreservation(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write([]byte(`{"access_token":"ACCESS_TOKEN", "scope": "user", "token_type": "bearer"}`))
|
||||
return
|
||||
}))
|
||||
defer ts.Close()
|
||||
conf := newConf(ts.URL)
|
||||
const oldRefreshToken = "OLD_REFRESH_TOKEN"
|
||||
tkr := &tokenRefresher{
|
||||
conf: conf,
|
||||
ctx: context.Background(),
|
||||
refreshToken: oldRefreshToken,
|
||||
}
|
||||
_, err := tkr.Token()
|
||||
if err != nil {
|
||||
t.Fatalf("got err = %v; want none", err)
|
||||
}
|
||||
if tkr.refreshToken != oldRefreshToken {
|
||||
t.Errorf("tokenRefresher.refreshToken = %q; want %q", tkr.refreshToken, oldRefreshToken)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigClientWithToken(t *testing.T) {
|
||||
tok := &Token{
|
||||
AccessToken: "abc123",
|
||||
}
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("Authorization"), fmt.Sprintf("Bearer %s", tok.AccessToken); got != want {
|
||||
t.Errorf("Authorization header = %q; want %q", got, want)
|
||||
}
|
||||
return
|
||||
}))
|
||||
defer ts.Close()
|
||||
conf := newConf(ts.URL)
|
||||
|
||||
c := conf.Client(context.Background(), tok)
|
||||
req, err := http.NewRequest("GET", ts.URL, nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
_, err = c.Do(req)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
158
vendor/golang.org/x/oauth2/token.go
generated
vendored
Normal file
158
vendor/golang.org/x/oauth2/token.go
generated
vendored
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package oauth2
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/oauth2/internal"
|
||||
)
|
||||
|
||||
// expiryDelta determines how earlier a token should be considered
|
||||
// expired than its actual expiration time. It is used to avoid late
|
||||
// expirations due to client-server time mismatches.
|
||||
const expiryDelta = 10 * time.Second
|
||||
|
||||
// Token represents the crendentials used to authorize
|
||||
// the requests to access protected resources on the OAuth 2.0
|
||||
// provider's backend.
|
||||
//
|
||||
// Most users of this package should not access fields of Token
|
||||
// directly. They're exported mostly for use by related packages
|
||||
// implementing derivative OAuth2 flows.
|
||||
type Token struct {
|
||||
// AccessToken is the token that authorizes and authenticates
|
||||
// the requests.
|
||||
AccessToken string `json:"access_token"`
|
||||
|
||||
// TokenType is the type of token.
|
||||
// The Type method returns either this or "Bearer", the default.
|
||||
TokenType string `json:"token_type,omitempty"`
|
||||
|
||||
// RefreshToken is a token that's used by the application
|
||||
// (as opposed to the user) to refresh the access token
|
||||
// if it expires.
|
||||
RefreshToken string `json:"refresh_token,omitempty"`
|
||||
|
||||
// Expiry is the optional expiration time of the access token.
|
||||
//
|
||||
// If zero, TokenSource implementations will reuse the same
|
||||
// token forever and RefreshToken or equivalent
|
||||
// mechanisms for that TokenSource will not be used.
|
||||
Expiry time.Time `json:"expiry,omitempty"`
|
||||
|
||||
// raw optionally contains extra metadata from the server
|
||||
// when updating a token.
|
||||
raw interface{}
|
||||
}
|
||||
|
||||
// Type returns t.TokenType if non-empty, else "Bearer".
|
||||
func (t *Token) Type() string {
|
||||
if strings.EqualFold(t.TokenType, "bearer") {
|
||||
return "Bearer"
|
||||
}
|
||||
if strings.EqualFold(t.TokenType, "mac") {
|
||||
return "MAC"
|
||||
}
|
||||
if strings.EqualFold(t.TokenType, "basic") {
|
||||
return "Basic"
|
||||
}
|
||||
if t.TokenType != "" {
|
||||
return t.TokenType
|
||||
}
|
||||
return "Bearer"
|
||||
}
|
||||
|
||||
// SetAuthHeader sets the Authorization header to r using the access
|
||||
// token in t.
|
||||
//
|
||||
// This method is unnecessary when using Transport or an HTTP Client
|
||||
// returned by this package.
|
||||
func (t *Token) SetAuthHeader(r *http.Request) {
|
||||
r.Header.Set("Authorization", t.Type()+" "+t.AccessToken)
|
||||
}
|
||||
|
||||
// WithExtra returns a new Token that's a clone of t, but using the
|
||||
// provided raw extra map. This is only intended for use by packages
|
||||
// implementing derivative OAuth2 flows.
|
||||
func (t *Token) WithExtra(extra interface{}) *Token {
|
||||
t2 := new(Token)
|
||||
*t2 = *t
|
||||
t2.raw = extra
|
||||
return t2
|
||||
}
|
||||
|
||||
// Extra returns an extra field.
|
||||
// Extra fields are key-value pairs returned by the server as a
|
||||
// part of the token retrieval response.
|
||||
func (t *Token) Extra(key string) interface{} {
|
||||
if raw, ok := t.raw.(map[string]interface{}); ok {
|
||||
return raw[key]
|
||||
}
|
||||
|
||||
vals, ok := t.raw.(url.Values)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
v := vals.Get(key)
|
||||
switch s := strings.TrimSpace(v); strings.Count(s, ".") {
|
||||
case 0: // Contains no "."; try to parse as int
|
||||
if i, err := strconv.ParseInt(s, 10, 64); err == nil {
|
||||
return i
|
||||
}
|
||||
case 1: // Contains a single "."; try to parse as float
|
||||
if f, err := strconv.ParseFloat(s, 64); err == nil {
|
||||
return f
|
||||
}
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// expired reports whether the token is expired.
|
||||
// t must be non-nil.
|
||||
func (t *Token) expired() bool {
|
||||
if t.Expiry.IsZero() {
|
||||
return false
|
||||
}
|
||||
return t.Expiry.Add(-expiryDelta).Before(time.Now())
|
||||
}
|
||||
|
||||
// Valid reports whether t is non-nil, has an AccessToken, and is not expired.
|
||||
func (t *Token) Valid() bool {
|
||||
return t != nil && t.AccessToken != "" && !t.expired()
|
||||
}
|
||||
|
||||
// tokenFromInternal maps an *internal.Token struct into
|
||||
// a *Token struct.
|
||||
func tokenFromInternal(t *internal.Token) *Token {
|
||||
if t == nil {
|
||||
return nil
|
||||
}
|
||||
return &Token{
|
||||
AccessToken: t.AccessToken,
|
||||
TokenType: t.TokenType,
|
||||
RefreshToken: t.RefreshToken,
|
||||
Expiry: t.Expiry,
|
||||
raw: t.Raw,
|
||||
}
|
||||
}
|
||||
|
||||
// retrieveToken takes a *Config and uses that to retrieve an *internal.Token.
|
||||
// This token is then mapped from *internal.Token into an *oauth2.Token which is returned along
|
||||
// with an error..
|
||||
func retrieveToken(ctx context.Context, c *Config, v url.Values) (*Token, error) {
|
||||
tk, err := internal.RetrieveToken(ctx, c.ClientID, c.ClientSecret, c.Endpoint.TokenURL, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tokenFromInternal(tk), nil
|
||||
}
|
||||
72
vendor/golang.org/x/oauth2/token_test.go
generated
vendored
Normal file
72
vendor/golang.org/x/oauth2/token_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package oauth2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestTokenExtra(t *testing.T) {
|
||||
type testCase struct {
|
||||
key string
|
||||
val interface{}
|
||||
want interface{}
|
||||
}
|
||||
const key = "extra-key"
|
||||
cases := []testCase{
|
||||
{key: key, val: "abc", want: "abc"},
|
||||
{key: key, val: 123, want: 123},
|
||||
{key: key, val: "", want: ""},
|
||||
{key: "other-key", val: "def", want: nil},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
extra := make(map[string]interface{})
|
||||
extra[tc.key] = tc.val
|
||||
tok := &Token{raw: extra}
|
||||
if got, want := tok.Extra(key), tc.want; got != want {
|
||||
t.Errorf("Extra(%q) = %q; want %q", key, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenExpiry(t *testing.T) {
|
||||
now := time.Now()
|
||||
cases := []struct {
|
||||
name string
|
||||
tok *Token
|
||||
want bool
|
||||
}{
|
||||
{name: "12 seconds", tok: &Token{Expiry: now.Add(12 * time.Second)}, want: false},
|
||||
{name: "10 seconds", tok: &Token{Expiry: now.Add(expiryDelta)}, want: true},
|
||||
{name: "-1 hour", tok: &Token{Expiry: now.Add(-1 * time.Hour)}, want: true},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
if got, want := tc.tok.expired(), tc.want; got != want {
|
||||
t.Errorf("expired (%q) = %v; want %v", tc.name, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenTypeMethod(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
tok *Token
|
||||
want string
|
||||
}{
|
||||
{name: "bearer-mixed_case", tok: &Token{TokenType: "beAREr"}, want: "Bearer"},
|
||||
{name: "default-bearer", tok: &Token{}, want: "Bearer"},
|
||||
{name: "basic", tok: &Token{TokenType: "basic"}, want: "Basic"},
|
||||
{name: "basic-capitalized", tok: &Token{TokenType: "Basic"}, want: "Basic"},
|
||||
{name: "mac", tok: &Token{TokenType: "mac"}, want: "MAC"},
|
||||
{name: "mac-caps", tok: &Token{TokenType: "MAC"}, want: "MAC"},
|
||||
{name: "mac-mixed_case", tok: &Token{TokenType: "mAc"}, want: "MAC"},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
if got, want := tc.tok.Type(), tc.want; got != want {
|
||||
t.Errorf("TokenType(%q) = %v; want %v", tc.name, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
132
vendor/golang.org/x/oauth2/transport.go
generated
vendored
Normal file
132
vendor/golang.org/x/oauth2/transport.go
generated
vendored
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package oauth2
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Transport is an http.RoundTripper that makes OAuth 2.0 HTTP requests,
|
||||
// wrapping a base RoundTripper and adding an Authorization header
|
||||
// with a token from the supplied Sources.
|
||||
//
|
||||
// Transport is a low-level mechanism. Most code will use the
|
||||
// higher-level Config.Client method instead.
|
||||
type Transport struct {
|
||||
// Source supplies the token to add to outgoing requests'
|
||||
// Authorization headers.
|
||||
Source TokenSource
|
||||
|
||||
// Base is the base RoundTripper used to make HTTP requests.
|
||||
// If nil, http.DefaultTransport is used.
|
||||
Base http.RoundTripper
|
||||
|
||||
mu sync.Mutex // guards modReq
|
||||
modReq map[*http.Request]*http.Request // original -> modified
|
||||
}
|
||||
|
||||
// RoundTrip authorizes and authenticates the request with an
|
||||
// access token. If no token exists or token is expired,
|
||||
// tries to refresh/fetch a new token.
|
||||
func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
if t.Source == nil {
|
||||
return nil, errors.New("oauth2: Transport's Source is nil")
|
||||
}
|
||||
token, err := t.Source.Token()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req2 := cloneRequest(req) // per RoundTripper contract
|
||||
token.SetAuthHeader(req2)
|
||||
t.setModReq(req, req2)
|
||||
res, err := t.base().RoundTrip(req2)
|
||||
if err != nil {
|
||||
t.setModReq(req, nil)
|
||||
return nil, err
|
||||
}
|
||||
res.Body = &onEOFReader{
|
||||
rc: res.Body,
|
||||
fn: func() { t.setModReq(req, nil) },
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// CancelRequest cancels an in-flight request by closing its connection.
|
||||
func (t *Transport) CancelRequest(req *http.Request) {
|
||||
type canceler interface {
|
||||
CancelRequest(*http.Request)
|
||||
}
|
||||
if cr, ok := t.base().(canceler); ok {
|
||||
t.mu.Lock()
|
||||
modReq := t.modReq[req]
|
||||
delete(t.modReq, req)
|
||||
t.mu.Unlock()
|
||||
cr.CancelRequest(modReq)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Transport) base() http.RoundTripper {
|
||||
if t.Base != nil {
|
||||
return t.Base
|
||||
}
|
||||
return http.DefaultTransport
|
||||
}
|
||||
|
||||
func (t *Transport) setModReq(orig, mod *http.Request) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
if t.modReq == nil {
|
||||
t.modReq = make(map[*http.Request]*http.Request)
|
||||
}
|
||||
if mod == nil {
|
||||
delete(t.modReq, orig)
|
||||
} else {
|
||||
t.modReq[orig] = mod
|
||||
}
|
||||
}
|
||||
|
||||
// cloneRequest returns a clone of the provided *http.Request.
|
||||
// The clone is a shallow copy of the struct and its Header map.
|
||||
func cloneRequest(r *http.Request) *http.Request {
|
||||
// shallow copy of the struct
|
||||
r2 := new(http.Request)
|
||||
*r2 = *r
|
||||
// deep copy of the Header
|
||||
r2.Header = make(http.Header, len(r.Header))
|
||||
for k, s := range r.Header {
|
||||
r2.Header[k] = append([]string(nil), s...)
|
||||
}
|
||||
return r2
|
||||
}
|
||||
|
||||
type onEOFReader struct {
|
||||
rc io.ReadCloser
|
||||
fn func()
|
||||
}
|
||||
|
||||
func (r *onEOFReader) Read(p []byte) (n int, err error) {
|
||||
n, err = r.rc.Read(p)
|
||||
if err == io.EOF {
|
||||
r.runFunc()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (r *onEOFReader) Close() error {
|
||||
err := r.rc.Close()
|
||||
r.runFunc()
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *onEOFReader) runFunc() {
|
||||
if fn := r.fn; fn != nil {
|
||||
fn()
|
||||
r.fn = nil
|
||||
}
|
||||
}
|
||||
108
vendor/golang.org/x/oauth2/transport_test.go
generated
vendored
Normal file
108
vendor/golang.org/x/oauth2/transport_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
package oauth2
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type tokenSource struct{ token *Token }
|
||||
|
||||
func (t *tokenSource) Token() (*Token, error) {
|
||||
return t.token, nil
|
||||
}
|
||||
|
||||
func TestTransportNilTokenSource(t *testing.T) {
|
||||
tr := &Transport{}
|
||||
server := newMockServer(func(w http.ResponseWriter, r *http.Request) {})
|
||||
defer server.Close()
|
||||
client := &http.Client{Transport: tr}
|
||||
resp, err := client.Get(server.URL)
|
||||
if err == nil {
|
||||
t.Errorf("got no errors, want an error with nil token source")
|
||||
}
|
||||
if resp != nil {
|
||||
t.Errorf("Response = %v; want nil", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTransportTokenSource(t *testing.T) {
|
||||
ts := &tokenSource{
|
||||
token: &Token{
|
||||
AccessToken: "abc",
|
||||
},
|
||||
}
|
||||
tr := &Transport{
|
||||
Source: ts,
|
||||
}
|
||||
server := newMockServer(func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("Authorization"), "Bearer abc"; got != want {
|
||||
t.Errorf("Authorization header = %q; want %q", got, want)
|
||||
}
|
||||
})
|
||||
defer server.Close()
|
||||
client := &http.Client{Transport: tr}
|
||||
res, err := client.Get(server.URL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
res.Body.Close()
|
||||
}
|
||||
|
||||
// Test for case-sensitive token types, per https://github.com/golang/oauth2/issues/113
|
||||
func TestTransportTokenSourceTypes(t *testing.T) {
|
||||
const val = "abc"
|
||||
tests := []struct {
|
||||
key string
|
||||
val string
|
||||
want string
|
||||
}{
|
||||
{key: "bearer", val: val, want: "Bearer abc"},
|
||||
{key: "mac", val: val, want: "MAC abc"},
|
||||
{key: "basic", val: val, want: "Basic abc"},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
ts := &tokenSource{
|
||||
token: &Token{
|
||||
AccessToken: tc.val,
|
||||
TokenType: tc.key,
|
||||
},
|
||||
}
|
||||
tr := &Transport{
|
||||
Source: ts,
|
||||
}
|
||||
server := newMockServer(func(w http.ResponseWriter, r *http.Request) {
|
||||
if got, want := r.Header.Get("Authorization"), tc.want; got != want {
|
||||
t.Errorf("Authorization header (%q) = %q; want %q", val, got, want)
|
||||
}
|
||||
})
|
||||
defer server.Close()
|
||||
client := &http.Client{Transport: tr}
|
||||
res, err := client.Get(server.URL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
res.Body.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenValidNoAccessToken(t *testing.T) {
|
||||
token := &Token{}
|
||||
if token.Valid() {
|
||||
t.Errorf("got valid with no access token; want invalid")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpiredWithExpiry(t *testing.T) {
|
||||
token := &Token{
|
||||
Expiry: time.Now().Add(-5 * time.Hour),
|
||||
}
|
||||
if token.Valid() {
|
||||
t.Errorf("got valid with expired token; want invalid")
|
||||
}
|
||||
}
|
||||
|
||||
func newMockServer(handler func(w http.ResponseWriter, r *http.Request)) *httptest.Server {
|
||||
return httptest.NewServer(http.HandlerFunc(handler))
|
||||
}
|
||||
1
vendor/golang.org/x/sys/unix/mkerrors.sh
generated
vendored
1
vendor/golang.org/x/sys/unix/mkerrors.sh
generated
vendored
|
|
@ -84,6 +84,7 @@ includes_FreeBSD='
|
|||
#include <sys/sockio.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/bpf.h>
|
||||
|
|
|
|||
1
vendor/golang.org/x/sys/unix/syscall_linux.go
generated
vendored
1
vendor/golang.org/x/sys/unix/syscall_linux.go
generated
vendored
|
|
@ -1262,6 +1262,7 @@ func Getpgrp() (pid int) {
|
|||
//sys PivotRoot(newroot string, putold string) (err error) = SYS_PIVOT_ROOT
|
||||
//sysnb prlimit(pid int, resource int, newlimit *Rlimit, old *Rlimit) (err error) = SYS_PRLIMIT64
|
||||
//sys Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (err error)
|
||||
//sys Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) = SYS_PSELECT6
|
||||
//sys read(fd int, p []byte) (n int, err error)
|
||||
//sys Removexattr(path string, attr string) (err error)
|
||||
//sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error)
|
||||
|
|
|
|||
7
vendor/golang.org/x/sys/unix/syscall_linux_arm64.go
generated
vendored
7
vendor/golang.org/x/sys/unix/syscall_linux_arm64.go
generated
vendored
|
|
@ -21,7 +21,12 @@ package unix
|
|||
//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64
|
||||
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64
|
||||
//sys Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK
|
||||
//sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) = SYS_PSELECT6
|
||||
|
||||
func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) {
|
||||
ts := Timespec{Sec: timeout.Sec, Nsec: timeout.Usec * 1000}
|
||||
return Pselect(nfd, r, w, e, &ts, nil)
|
||||
}
|
||||
|
||||
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)
|
||||
//sys Setfsgid(gid int) (err error)
|
||||
//sys Setfsuid(uid int) (err error)
|
||||
|
|
|
|||
7
vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go
generated
vendored
7
vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go
generated
vendored
|
|
@ -23,7 +23,12 @@ package unix
|
|||
//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64
|
||||
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64
|
||||
//sys Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK
|
||||
//sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) = SYS_PSELECT6
|
||||
|
||||
func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) {
|
||||
ts := Timespec{Sec: timeout.Sec, Nsec: timeout.Usec * 1000}
|
||||
return Pselect(nfd, r, w, e, &ts, nil)
|
||||
}
|
||||
|
||||
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)
|
||||
//sys Setfsgid(gid int) (err error)
|
||||
//sys Setfsuid(uid int) (err error)
|
||||
|
|
|
|||
43
vendor/golang.org/x/sys/unix/zerrors_freebsd_386.go
generated
vendored
43
vendor/golang.org/x/sys/unix/zerrors_freebsd_386.go
generated
vendored
|
|
@ -981,6 +981,49 @@ const (
|
|||
MAP_STACK = 0x400
|
||||
MCL_CURRENT = 0x1
|
||||
MCL_FUTURE = 0x2
|
||||
MNT_ACLS = 0x8000000
|
||||
MNT_ASYNC = 0x40
|
||||
MNT_AUTOMOUNTED = 0x200000000
|
||||
MNT_BYFSID = 0x8000000
|
||||
MNT_CMDFLAGS = 0xd0f0000
|
||||
MNT_DEFEXPORTED = 0x200
|
||||
MNT_DELEXPORT = 0x20000
|
||||
MNT_EXKERB = 0x800
|
||||
MNT_EXPORTANON = 0x400
|
||||
MNT_EXPORTED = 0x100
|
||||
MNT_EXPUBLIC = 0x20000000
|
||||
MNT_EXRDONLY = 0x80
|
||||
MNT_FORCE = 0x80000
|
||||
MNT_GJOURNAL = 0x2000000
|
||||
MNT_IGNORE = 0x800000
|
||||
MNT_LAZY = 0x3
|
||||
MNT_LOCAL = 0x1000
|
||||
MNT_MULTILABEL = 0x4000000
|
||||
MNT_NFS4ACLS = 0x10
|
||||
MNT_NOATIME = 0x10000000
|
||||
MNT_NOCLUSTERR = 0x40000000
|
||||
MNT_NOCLUSTERW = 0x80000000
|
||||
MNT_NOEXEC = 0x4
|
||||
MNT_NONBUSY = 0x4000000
|
||||
MNT_NOSUID = 0x8
|
||||
MNT_NOSYMFOLLOW = 0x400000
|
||||
MNT_NOWAIT = 0x2
|
||||
MNT_QUOTA = 0x2000
|
||||
MNT_RDONLY = 0x1
|
||||
MNT_RELOAD = 0x40000
|
||||
MNT_ROOTFS = 0x4000
|
||||
MNT_SNAPSHOT = 0x1000000
|
||||
MNT_SOFTDEP = 0x200000
|
||||
MNT_SUIDDIR = 0x100000
|
||||
MNT_SUJ = 0x100000000
|
||||
MNT_SUSPEND = 0x4
|
||||
MNT_SYNCHRONOUS = 0x2
|
||||
MNT_UNION = 0x20
|
||||
MNT_UPDATE = 0x10000
|
||||
MNT_UPDATEMASK = 0x2d8d0807e
|
||||
MNT_USER = 0x8000
|
||||
MNT_VISFLAGMASK = 0x3fef0ffff
|
||||
MNT_WAIT = 0x1
|
||||
MSG_CMSG_CLOEXEC = 0x40000
|
||||
MSG_COMPAT = 0x8000
|
||||
MSG_CTRUNC = 0x20
|
||||
|
|
|
|||
43
vendor/golang.org/x/sys/unix/zerrors_freebsd_amd64.go
generated
vendored
43
vendor/golang.org/x/sys/unix/zerrors_freebsd_amd64.go
generated
vendored
|
|
@ -982,6 +982,49 @@ const (
|
|||
MAP_STACK = 0x400
|
||||
MCL_CURRENT = 0x1
|
||||
MCL_FUTURE = 0x2
|
||||
MNT_ACLS = 0x8000000
|
||||
MNT_ASYNC = 0x40
|
||||
MNT_AUTOMOUNTED = 0x200000000
|
||||
MNT_BYFSID = 0x8000000
|
||||
MNT_CMDFLAGS = 0xd0f0000
|
||||
MNT_DEFEXPORTED = 0x200
|
||||
MNT_DELEXPORT = 0x20000
|
||||
MNT_EXKERB = 0x800
|
||||
MNT_EXPORTANON = 0x400
|
||||
MNT_EXPORTED = 0x100
|
||||
MNT_EXPUBLIC = 0x20000000
|
||||
MNT_EXRDONLY = 0x80
|
||||
MNT_FORCE = 0x80000
|
||||
MNT_GJOURNAL = 0x2000000
|
||||
MNT_IGNORE = 0x800000
|
||||
MNT_LAZY = 0x3
|
||||
MNT_LOCAL = 0x1000
|
||||
MNT_MULTILABEL = 0x4000000
|
||||
MNT_NFS4ACLS = 0x10
|
||||
MNT_NOATIME = 0x10000000
|
||||
MNT_NOCLUSTERR = 0x40000000
|
||||
MNT_NOCLUSTERW = 0x80000000
|
||||
MNT_NOEXEC = 0x4
|
||||
MNT_NONBUSY = 0x4000000
|
||||
MNT_NOSUID = 0x8
|
||||
MNT_NOSYMFOLLOW = 0x400000
|
||||
MNT_NOWAIT = 0x2
|
||||
MNT_QUOTA = 0x2000
|
||||
MNT_RDONLY = 0x1
|
||||
MNT_RELOAD = 0x40000
|
||||
MNT_ROOTFS = 0x4000
|
||||
MNT_SNAPSHOT = 0x1000000
|
||||
MNT_SOFTDEP = 0x200000
|
||||
MNT_SUIDDIR = 0x100000
|
||||
MNT_SUJ = 0x100000000
|
||||
MNT_SUSPEND = 0x4
|
||||
MNT_SYNCHRONOUS = 0x2
|
||||
MNT_UNION = 0x20
|
||||
MNT_UPDATE = 0x10000
|
||||
MNT_UPDATEMASK = 0x2d8d0807e
|
||||
MNT_USER = 0x8000
|
||||
MNT_VISFLAGMASK = 0x3fef0ffff
|
||||
MNT_WAIT = 0x1
|
||||
MSG_CMSG_CLOEXEC = 0x40000
|
||||
MSG_COMPAT = 0x8000
|
||||
MSG_CTRUNC = 0x20
|
||||
|
|
|
|||
43
vendor/golang.org/x/sys/unix/zerrors_freebsd_arm.go
generated
vendored
43
vendor/golang.org/x/sys/unix/zerrors_freebsd_arm.go
generated
vendored
|
|
@ -989,6 +989,49 @@ const (
|
|||
MAP_STACK = 0x400
|
||||
MCL_CURRENT = 0x1
|
||||
MCL_FUTURE = 0x2
|
||||
MNT_ACLS = 0x8000000
|
||||
MNT_ASYNC = 0x40
|
||||
MNT_AUTOMOUNTED = 0x200000000
|
||||
MNT_BYFSID = 0x8000000
|
||||
MNT_CMDFLAGS = 0xd0f0000
|
||||
MNT_DEFEXPORTED = 0x200
|
||||
MNT_DELEXPORT = 0x20000
|
||||
MNT_EXKERB = 0x800
|
||||
MNT_EXPORTANON = 0x400
|
||||
MNT_EXPORTED = 0x100
|
||||
MNT_EXPUBLIC = 0x20000000
|
||||
MNT_EXRDONLY = 0x80
|
||||
MNT_FORCE = 0x80000
|
||||
MNT_GJOURNAL = 0x2000000
|
||||
MNT_IGNORE = 0x800000
|
||||
MNT_LAZY = 0x3
|
||||
MNT_LOCAL = 0x1000
|
||||
MNT_MULTILABEL = 0x4000000
|
||||
MNT_NFS4ACLS = 0x10
|
||||
MNT_NOATIME = 0x10000000
|
||||
MNT_NOCLUSTERR = 0x40000000
|
||||
MNT_NOCLUSTERW = 0x80000000
|
||||
MNT_NOEXEC = 0x4
|
||||
MNT_NONBUSY = 0x4000000
|
||||
MNT_NOSUID = 0x8
|
||||
MNT_NOSYMFOLLOW = 0x400000
|
||||
MNT_NOWAIT = 0x2
|
||||
MNT_QUOTA = 0x2000
|
||||
MNT_RDONLY = 0x1
|
||||
MNT_RELOAD = 0x40000
|
||||
MNT_ROOTFS = 0x4000
|
||||
MNT_SNAPSHOT = 0x1000000
|
||||
MNT_SOFTDEP = 0x200000
|
||||
MNT_SUIDDIR = 0x100000
|
||||
MNT_SUJ = 0x100000000
|
||||
MNT_SUSPEND = 0x4
|
||||
MNT_SYNCHRONOUS = 0x2
|
||||
MNT_UNION = 0x20
|
||||
MNT_UPDATE = 0x10000
|
||||
MNT_UPDATEMASK = 0x2d8d0807e
|
||||
MNT_USER = 0x8000
|
||||
MNT_VISFLAGMASK = 0x3fef0ffff
|
||||
MNT_WAIT = 0x1
|
||||
MSG_CMSG_CLOEXEC = 0x40000
|
||||
MSG_COMPAT = 0x8000
|
||||
MSG_CTRUNC = 0x20
|
||||
|
|
|
|||
11
vendor/golang.org/x/sys/unix/zsyscall_linux_386.go
generated
vendored
11
vendor/golang.org/x/sys/unix/zsyscall_linux_386.go
generated
vendored
|
|
@ -1035,6 +1035,17 @@ func Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (
|
|||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
|
||||
r0, _, e1 := Syscall6(SYS_PSELECT6, uintptr(nfd), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func read(fd int, p []byte) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(p) > 0 {
|
||||
|
|
|
|||
11
vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go
generated
vendored
11
vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go
generated
vendored
|
|
@ -1035,6 +1035,17 @@ func Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (
|
|||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
|
||||
r0, _, e1 := Syscall6(SYS_PSELECT6, uintptr(nfd), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func read(fd int, p []byte) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(p) > 0 {
|
||||
|
|
|
|||
11
vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go
generated
vendored
11
vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go
generated
vendored
|
|
@ -1035,6 +1035,17 @@ func Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (
|
|||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
|
||||
r0, _, e1 := Syscall6(SYS_PSELECT6, uintptr(nfd), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func read(fd int, p []byte) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(p) > 0 {
|
||||
|
|
|
|||
22
vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go
generated
vendored
22
vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go
generated
vendored
|
|
@ -1035,6 +1035,17 @@ func Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (
|
|||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
|
||||
r0, _, e1 := Syscall6(SYS_PSELECT6, uintptr(nfd), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func read(fd int, p []byte) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(p) > 0 {
|
||||
|
|
@ -1667,17 +1678,6 @@ func Seek(fd int, offset int64, whence int) (off int64, err error) {
|
|||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) {
|
||||
r0, _, e1 := Syscall6(SYS_PSELECT6, uintptr(nfd), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
|
||||
r0, _, e1 := Syscall6(SYS_SENDFILE, uintptr(outfd), uintptr(infd), uintptr(unsafe.Pointer(offset)), uintptr(count), 0, 0)
|
||||
written = int(r0)
|
||||
|
|
|
|||
11
vendor/golang.org/x/sys/unix/zsyscall_linux_mips.go
generated
vendored
11
vendor/golang.org/x/sys/unix/zsyscall_linux_mips.go
generated
vendored
|
|
@ -1035,6 +1035,17 @@ func Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (
|
|||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
|
||||
r0, _, e1 := Syscall6(SYS_PSELECT6, uintptr(nfd), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func read(fd int, p []byte) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(p) > 0 {
|
||||
|
|
|
|||
22
vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go
generated
vendored
22
vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go
generated
vendored
|
|
@ -1035,6 +1035,17 @@ func Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (
|
|||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
|
||||
r0, _, e1 := Syscall6(SYS_PSELECT6, uintptr(nfd), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func read(fd int, p []byte) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(p) > 0 {
|
||||
|
|
@ -1677,17 +1688,6 @@ func Seek(fd int, offset int64, whence int) (off int64, err error) {
|
|||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) {
|
||||
r0, _, e1 := Syscall6(SYS_PSELECT6, uintptr(nfd), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
|
||||
r0, _, e1 := Syscall6(SYS_SENDFILE, uintptr(outfd), uintptr(infd), uintptr(unsafe.Pointer(offset)), uintptr(count), 0, 0)
|
||||
written = int(r0)
|
||||
|
|
|
|||
22
vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go
generated
vendored
22
vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go
generated
vendored
|
|
@ -1035,6 +1035,17 @@ func Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (
|
|||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
|
||||
r0, _, e1 := Syscall6(SYS_PSELECT6, uintptr(nfd), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func read(fd int, p []byte) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(p) > 0 {
|
||||
|
|
@ -1677,17 +1688,6 @@ func Seek(fd int, offset int64, whence int) (off int64, err error) {
|
|||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error) {
|
||||
r0, _, e1 := Syscall6(SYS_PSELECT6, uintptr(nfd), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), 0)
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
|
||||
r0, _, e1 := Syscall6(SYS_SENDFILE, uintptr(outfd), uintptr(infd), uintptr(unsafe.Pointer(offset)), uintptr(count), 0, 0)
|
||||
written = int(r0)
|
||||
|
|
|
|||
11
vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go
generated
vendored
11
vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go
generated
vendored
|
|
@ -1035,6 +1035,17 @@ func Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (
|
|||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
|
||||
r0, _, e1 := Syscall6(SYS_PSELECT6, uintptr(nfd), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func read(fd int, p []byte) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(p) > 0 {
|
||||
|
|
|
|||
11
vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go
generated
vendored
11
vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go
generated
vendored
|
|
@ -1035,6 +1035,17 @@ func Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (
|
|||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
|
||||
r0, _, e1 := Syscall6(SYS_PSELECT6, uintptr(nfd), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func read(fd int, p []byte) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(p) > 0 {
|
||||
|
|
|
|||
11
vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go
generated
vendored
11
vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go
generated
vendored
|
|
@ -1035,6 +1035,17 @@ func Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (
|
|||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
|
||||
r0, _, e1 := Syscall6(SYS_PSELECT6, uintptr(nfd), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func read(fd int, p []byte) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(p) > 0 {
|
||||
|
|
|
|||
11
vendor/golang.org/x/sys/unix/zsyscall_linux_s390x.go
generated
vendored
11
vendor/golang.org/x/sys/unix/zsyscall_linux_s390x.go
generated
vendored
|
|
@ -1035,6 +1035,17 @@ func Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (
|
|||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {
|
||||
r0, _, e1 := Syscall6(SYS_PSELECT6, uintptr(nfd), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func read(fd int, p []byte) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(p) > 0 {
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue