vendor: revendor
This commit is contained in:
76
vendor/google.golang.org/appengine/appengine.go
generated
vendored
Normal file
76
vendor/google.golang.org/appengine/appengine.go
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package appengine provides basic functionality for Google App Engine.
|
||||
//
|
||||
// For more information on how to write Go apps for Google App Engine, see:
|
||||
// https://cloud.google.com/appengine/docs/go/
|
||||
package appengine // import "google.golang.org/appengine"
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"google.golang.org/appengine/internal"
|
||||
)
|
||||
|
||||
// IsDevAppServer reports whether the App Engine app is running in the
|
||||
// development App Server.
|
||||
func IsDevAppServer() bool {
|
||||
return internal.IsDevAppServer()
|
||||
}
|
||||
|
||||
// NewContext returns a context for an in-flight HTTP request.
|
||||
// This function is cheap.
|
||||
func NewContext(req *http.Request) context.Context {
|
||||
return WithContext(context.Background(), req)
|
||||
}
|
||||
|
||||
// WithContext returns a copy of the parent context
|
||||
// and associates it with an in-flight HTTP request.
|
||||
// This function is cheap.
|
||||
func WithContext(parent context.Context, req *http.Request) context.Context {
|
||||
return internal.WithContext(parent, req)
|
||||
}
|
||||
|
||||
// TODO(dsymonds): Add a Call function here? Otherwise other packages can't access internal.Call.
|
||||
|
||||
// BlobKey is a key for a blobstore blob.
|
||||
//
|
||||
// Conceptually, this type belongs in the blobstore package, but it lives in
|
||||
// the appengine package to avoid a circular dependency: blobstore depends on
|
||||
// datastore, and datastore needs to refer to the BlobKey type.
|
||||
type BlobKey string
|
||||
|
||||
// GeoPoint represents a location as latitude/longitude in degrees.
|
||||
type GeoPoint struct {
|
||||
Lat, Lng float64
|
||||
}
|
||||
|
||||
// Valid returns whether a GeoPoint is within [-90, 90] latitude and [-180, 180] longitude.
|
||||
func (g GeoPoint) Valid() bool {
|
||||
return -90 <= g.Lat && g.Lat <= 90 && -180 <= g.Lng && g.Lng <= 180
|
||||
}
|
||||
|
||||
// APICallFunc defines a function type for handling an API call.
|
||||
// See WithCallOverride.
|
||||
type APICallFunc func(ctx context.Context, service, method string, in, out proto.Message) error
|
||||
|
||||
// WithAPICallFunc returns a copy of the parent context
|
||||
// that will cause API calls to invoke f instead of their normal operation.
|
||||
//
|
||||
// This is intended for advanced users only.
|
||||
func WithAPICallFunc(ctx context.Context, f APICallFunc) context.Context {
|
||||
return internal.WithCallOverride(ctx, internal.CallOverrideFunc(f))
|
||||
}
|
||||
|
||||
// APICall performs an API call.
|
||||
//
|
||||
// This is not intended for general use; it is exported for use in conjunction
|
||||
// with WithAPICallFunc.
|
||||
func APICall(ctx context.Context, service, method string, in, out proto.Message) error {
|
||||
return internal.Call(ctx, service, method, in, out)
|
||||
}
|
||||
56
vendor/google.golang.org/appengine/appengine_vm.go
generated
vendored
Normal file
56
vendor/google.golang.org/appengine/appengine_vm.go
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright 2015 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
|
||||
package appengine
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"google.golang.org/appengine/internal"
|
||||
)
|
||||
|
||||
// The comment below must not be changed.
|
||||
// It is used by go-app-builder to recognise that this package has
|
||||
// the Main function to use in the synthetic main.
|
||||
// The gophers party all night; the rabbits provide the beats.
|
||||
|
||||
// Main is the principal entry point for an app running in App Engine "flexible environment".
|
||||
// It installs a trivial health checker if one isn't already registered,
|
||||
// and starts listening on port 8080 (overridden by the $PORT environment
|
||||
// variable).
|
||||
//
|
||||
// See https://cloud.google.com/appengine/docs/flexible/custom-runtimes#health_check_requests
|
||||
// for details on how to do your own health checking.
|
||||
//
|
||||
// Main never returns.
|
||||
//
|
||||
// Main is designed so that the app's main package looks like this:
|
||||
//
|
||||
// package main
|
||||
//
|
||||
// import (
|
||||
// "google.golang.org/appengine"
|
||||
//
|
||||
// _ "myapp/package0"
|
||||
// _ "myapp/package1"
|
||||
// )
|
||||
//
|
||||
// func main() {
|
||||
// appengine.Main()
|
||||
// }
|
||||
//
|
||||
// The "myapp/packageX" packages are expected to register HTTP handlers
|
||||
// in their init functions.
|
||||
func Main() {
|
||||
internal.Main()
|
||||
}
|
||||
|
||||
// BackgroundContext returns a context not associated with a request.
|
||||
// This should only be used when not servicing a request.
|
||||
// This only works in App Engine "flexible environment".
|
||||
func BackgroundContext() context.Context {
|
||||
return internal.BackgroundContext()
|
||||
}
|
||||
46
vendor/google.golang.org/appengine/errors.go
generated
vendored
Normal file
46
vendor/google.golang.org/appengine/errors.go
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file provides error functions for common API failure modes.
|
||||
|
||||
package appengine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/appengine/internal"
|
||||
)
|
||||
|
||||
// IsOverQuota reports whether err represents an API call failure
|
||||
// due to insufficient available quota.
|
||||
func IsOverQuota(err error) bool {
|
||||
callErr, ok := err.(*internal.CallError)
|
||||
return ok && callErr.Code == 4
|
||||
}
|
||||
|
||||
// MultiError is returned by batch operations when there are errors with
|
||||
// particular elements. Errors will be in a one-to-one correspondence with
|
||||
// the input elements; successful elements will have a nil entry.
|
||||
type MultiError []error
|
||||
|
||||
func (m MultiError) Error() string {
|
||||
s, n := "", 0
|
||||
for _, e := range m {
|
||||
if e != nil {
|
||||
if n == 0 {
|
||||
s = e.Error()
|
||||
}
|
||||
n++
|
||||
}
|
||||
}
|
||||
switch n {
|
||||
case 0:
|
||||
return "(0 errors)"
|
||||
case 1:
|
||||
return s
|
||||
case 2:
|
||||
return s + " (and 1 other error)"
|
||||
}
|
||||
return fmt.Sprintf("%s (and %d other errors)", s, n-1)
|
||||
}
|
||||
142
vendor/google.golang.org/appengine/identity.go
generated
vendored
Normal file
142
vendor/google.golang.org/appengine/identity.go
generated
vendored
Normal file
@@ -0,0 +1,142 @@
|
||||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package appengine
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"google.golang.org/appengine/internal"
|
||||
pb "google.golang.org/appengine/internal/app_identity"
|
||||
modpb "google.golang.org/appengine/internal/modules"
|
||||
)
|
||||
|
||||
// AppID returns the application ID for the current application.
|
||||
// The string will be a plain application ID (e.g. "appid"), with a
|
||||
// domain prefix for custom domain deployments (e.g. "example.com:appid").
|
||||
func AppID(c context.Context) string { return internal.AppID(c) }
|
||||
|
||||
// DefaultVersionHostname returns the standard hostname of the default version
|
||||
// of the current application (e.g. "my-app.appspot.com"). This is suitable for
|
||||
// use in constructing URLs.
|
||||
func DefaultVersionHostname(c context.Context) string {
|
||||
return internal.DefaultVersionHostname(c)
|
||||
}
|
||||
|
||||
// ModuleName returns the module name of the current instance.
|
||||
func ModuleName(c context.Context) string {
|
||||
return internal.ModuleName(c)
|
||||
}
|
||||
|
||||
// ModuleHostname returns a hostname of a module instance.
|
||||
// If module is the empty string, it refers to the module of the current instance.
|
||||
// If version is empty, it refers to the version of the current instance if valid,
|
||||
// or the default version of the module of the current instance.
|
||||
// If instance is empty, ModuleHostname returns the load-balancing hostname.
|
||||
func ModuleHostname(c context.Context, module, version, instance string) (string, error) {
|
||||
req := &modpb.GetHostnameRequest{}
|
||||
if module != "" {
|
||||
req.Module = &module
|
||||
}
|
||||
if version != "" {
|
||||
req.Version = &version
|
||||
}
|
||||
if instance != "" {
|
||||
req.Instance = &instance
|
||||
}
|
||||
res := &modpb.GetHostnameResponse{}
|
||||
if err := internal.Call(c, "modules", "GetHostname", req, res); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return *res.Hostname, nil
|
||||
}
|
||||
|
||||
// VersionID returns the version ID for the current application.
|
||||
// It will be of the form "X.Y", where X is specified in app.yaml,
|
||||
// and Y is a number generated when each version of the app is uploaded.
|
||||
// It does not include a module name.
|
||||
func VersionID(c context.Context) string { return internal.VersionID(c) }
|
||||
|
||||
// InstanceID returns a mostly-unique identifier for this instance.
|
||||
func InstanceID() string { return internal.InstanceID() }
|
||||
|
||||
// Datacenter returns an identifier for the datacenter that the instance is running in.
|
||||
func Datacenter(c context.Context) string { return internal.Datacenter(c) }
|
||||
|
||||
// ServerSoftware returns the App Engine release version.
|
||||
// In production, it looks like "Google App Engine/X.Y.Z".
|
||||
// In the development appserver, it looks like "Development/X.Y".
|
||||
func ServerSoftware() string { return internal.ServerSoftware() }
|
||||
|
||||
// RequestID returns a string that uniquely identifies the request.
|
||||
func RequestID(c context.Context) string { return internal.RequestID(c) }
|
||||
|
||||
// AccessToken generates an OAuth2 access token for the specified scopes on
|
||||
// behalf of service account of this application. This token will expire after
|
||||
// the returned time.
|
||||
func AccessToken(c context.Context, scopes ...string) (token string, expiry time.Time, err error) {
|
||||
req := &pb.GetAccessTokenRequest{Scope: scopes}
|
||||
res := &pb.GetAccessTokenResponse{}
|
||||
|
||||
err = internal.Call(c, "app_identity_service", "GetAccessToken", req, res)
|
||||
if err != nil {
|
||||
return "", time.Time{}, err
|
||||
}
|
||||
return res.GetAccessToken(), time.Unix(res.GetExpirationTime(), 0), nil
|
||||
}
|
||||
|
||||
// Certificate represents a public certificate for the app.
|
||||
type Certificate struct {
|
||||
KeyName string
|
||||
Data []byte // PEM-encoded X.509 certificate
|
||||
}
|
||||
|
||||
// PublicCertificates retrieves the public certificates for the app.
|
||||
// They can be used to verify a signature returned by SignBytes.
|
||||
func PublicCertificates(c context.Context) ([]Certificate, error) {
|
||||
req := &pb.GetPublicCertificateForAppRequest{}
|
||||
res := &pb.GetPublicCertificateForAppResponse{}
|
||||
if err := internal.Call(c, "app_identity_service", "GetPublicCertificatesForApp", req, res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var cs []Certificate
|
||||
for _, pc := range res.PublicCertificateList {
|
||||
cs = append(cs, Certificate{
|
||||
KeyName: pc.GetKeyName(),
|
||||
Data: []byte(pc.GetX509CertificatePem()),
|
||||
})
|
||||
}
|
||||
return cs, nil
|
||||
}
|
||||
|
||||
// ServiceAccount returns a string representing the service account name, in
|
||||
// the form of an email address (typically app_id@appspot.gserviceaccount.com).
|
||||
func ServiceAccount(c context.Context) (string, error) {
|
||||
req := &pb.GetServiceAccountNameRequest{}
|
||||
res := &pb.GetServiceAccountNameResponse{}
|
||||
|
||||
err := internal.Call(c, "app_identity_service", "GetServiceAccountName", req, res)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return res.GetServiceAccountName(), err
|
||||
}
|
||||
|
||||
// SignBytes signs bytes using a private key unique to your application.
|
||||
func SignBytes(c context.Context, bytes []byte) (keyName string, signature []byte, err error) {
|
||||
req := &pb.SignForAppRequest{BytesToSign: bytes}
|
||||
res := &pb.SignForAppResponse{}
|
||||
|
||||
if err := internal.Call(c, "app_identity_service", "SignForApp", req, res); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
return res.GetKeyName(), res.GetSignatureBytes(), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
internal.RegisterErrorCodeMap("app_identity_service", pb.AppIdentityServiceError_ErrorCode_name)
|
||||
internal.RegisterErrorCodeMap("modules", modpb.ModulesServiceError_ErrorCode_name)
|
||||
}
|
||||
9
vendor/google.golang.org/appengine/internal/api_race_test.go
generated
vendored
9
vendor/google.golang.org/appengine/internal/api_race_test.go
generated
vendored
@@ -1,9 +0,0 @@
|
||||
// Copyright 2014 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build race
|
||||
|
||||
package internal
|
||||
|
||||
func init() { raceDetector = true }
|
||||
467
vendor/google.golang.org/appengine/internal/api_test.go
generated
vendored
467
vendor/google.golang.org/appengine/internal/api_test.go
generated
vendored
@@ -1,467 +0,0 @@
|
||||
// Copyright 2014 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
netcontext "golang.org/x/net/context"
|
||||
|
||||
basepb "google.golang.org/appengine/internal/base"
|
||||
remotepb "google.golang.org/appengine/internal/remote_api"
|
||||
)
|
||||
|
||||
const testTicketHeader = "X-Magic-Ticket-Header"
|
||||
|
||||
func init() {
|
||||
ticketHeader = testTicketHeader
|
||||
}
|
||||
|
||||
type fakeAPIHandler struct {
|
||||
hang chan int // used for RunSlowly RPC
|
||||
|
||||
LogFlushes int32 // atomic
|
||||
}
|
||||
|
||||
func (f *fakeAPIHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
writeResponse := func(res *remotepb.Response) {
|
||||
hresBody, err := proto.Marshal(res)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Failed encoding API response: %v", err), 500)
|
||||
return
|
||||
}
|
||||
w.Write(hresBody)
|
||||
}
|
||||
|
||||
if r.URL.Path != "/rpc_http" {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
hreqBody, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Bad body: %v", err), 500)
|
||||
return
|
||||
}
|
||||
apiReq := &remotepb.Request{}
|
||||
if err := proto.Unmarshal(hreqBody, apiReq); err != nil {
|
||||
http.Error(w, fmt.Sprintf("Bad encoded API request: %v", err), 500)
|
||||
return
|
||||
}
|
||||
if *apiReq.RequestId != "s3cr3t" {
|
||||
writeResponse(&remotepb.Response{
|
||||
RpcError: &remotepb.RpcError{
|
||||
Code: proto.Int32(int32(remotepb.RpcError_SECURITY_VIOLATION)),
|
||||
Detail: proto.String("bad security ticket"),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
if got, want := r.Header.Get(dapperHeader), "trace-001"; got != want {
|
||||
writeResponse(&remotepb.Response{
|
||||
RpcError: &remotepb.RpcError{
|
||||
Code: proto.Int32(int32(remotepb.RpcError_BAD_REQUEST)),
|
||||
Detail: proto.String(fmt.Sprintf("trace info = %q, want %q", got, want)),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
service, method := *apiReq.ServiceName, *apiReq.Method
|
||||
var resOut proto.Message
|
||||
if service == "actordb" && method == "LookupActor" {
|
||||
req := &basepb.StringProto{}
|
||||
res := &basepb.StringProto{}
|
||||
if err := proto.Unmarshal(apiReq.Request, req); err != nil {
|
||||
http.Error(w, fmt.Sprintf("Bad encoded request: %v", err), 500)
|
||||
return
|
||||
}
|
||||
if *req.Value == "Doctor Who" {
|
||||
res.Value = proto.String("David Tennant")
|
||||
}
|
||||
resOut = res
|
||||
}
|
||||
if service == "errors" {
|
||||
switch method {
|
||||
case "Non200":
|
||||
http.Error(w, "I'm a little teapot.", 418)
|
||||
return
|
||||
case "ShortResponse":
|
||||
w.Header().Set("Content-Length", "100")
|
||||
w.Write([]byte("way too short"))
|
||||
return
|
||||
case "OverQuota":
|
||||
writeResponse(&remotepb.Response{
|
||||
RpcError: &remotepb.RpcError{
|
||||
Code: proto.Int32(int32(remotepb.RpcError_OVER_QUOTA)),
|
||||
Detail: proto.String("you are hogging the resources!"),
|
||||
},
|
||||
})
|
||||
return
|
||||
case "RunSlowly":
|
||||
// TestAPICallRPCFailure creates f.hang, but does not strobe it
|
||||
// until Call returns with remotepb.RpcError_CANCELLED.
|
||||
// This is here to force a happens-before relationship between
|
||||
// the httptest server handler and shutdown.
|
||||
<-f.hang
|
||||
resOut = &basepb.VoidProto{}
|
||||
}
|
||||
}
|
||||
if service == "logservice" && method == "Flush" {
|
||||
// Pretend log flushing is slow.
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
atomic.AddInt32(&f.LogFlushes, 1)
|
||||
resOut = &basepb.VoidProto{}
|
||||
}
|
||||
|
||||
encOut, err := proto.Marshal(resOut)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Failed encoding response: %v", err), 500)
|
||||
return
|
||||
}
|
||||
writeResponse(&remotepb.Response{
|
||||
Response: encOut,
|
||||
})
|
||||
}
|
||||
|
||||
func setup() (f *fakeAPIHandler, c *context, cleanup func()) {
|
||||
f = &fakeAPIHandler{}
|
||||
srv := httptest.NewServer(f)
|
||||
u, err := url.Parse(srv.URL + apiPath)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("url.Parse(%q): %v", srv.URL+apiPath, err))
|
||||
}
|
||||
return f, &context{
|
||||
req: &http.Request{
|
||||
Header: http.Header{
|
||||
ticketHeader: []string{"s3cr3t"},
|
||||
dapperHeader: []string{"trace-001"},
|
||||
},
|
||||
},
|
||||
apiURL: u,
|
||||
}, srv.Close
|
||||
}
|
||||
|
||||
func TestAPICall(t *testing.T) {
|
||||
_, c, cleanup := setup()
|
||||
defer cleanup()
|
||||
|
||||
req := &basepb.StringProto{
|
||||
Value: proto.String("Doctor Who"),
|
||||
}
|
||||
res := &basepb.StringProto{}
|
||||
err := Call(toContext(c), "actordb", "LookupActor", req, res)
|
||||
if err != nil {
|
||||
t.Fatalf("API call failed: %v", err)
|
||||
}
|
||||
if got, want := *res.Value, "David Tennant"; got != want {
|
||||
t.Errorf("Response is %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPICallRPCFailure(t *testing.T) {
|
||||
f, c, cleanup := setup()
|
||||
defer cleanup()
|
||||
|
||||
testCases := []struct {
|
||||
method string
|
||||
code remotepb.RpcError_ErrorCode
|
||||
}{
|
||||
{"Non200", remotepb.RpcError_UNKNOWN},
|
||||
{"ShortResponse", remotepb.RpcError_UNKNOWN},
|
||||
{"OverQuota", remotepb.RpcError_OVER_QUOTA},
|
||||
{"RunSlowly", remotepb.RpcError_CANCELLED},
|
||||
}
|
||||
f.hang = make(chan int) // only for RunSlowly
|
||||
for _, tc := range testCases {
|
||||
ctx, _ := netcontext.WithTimeout(toContext(c), 100*time.Millisecond)
|
||||
err := Call(ctx, "errors", tc.method, &basepb.VoidProto{}, &basepb.VoidProto{})
|
||||
ce, ok := err.(*CallError)
|
||||
if !ok {
|
||||
t.Errorf("%s: API call error is %T (%v), want *CallError", tc.method, err, err)
|
||||
continue
|
||||
}
|
||||
if ce.Code != int32(tc.code) {
|
||||
t.Errorf("%s: ce.Code = %d, want %d", tc.method, ce.Code, tc.code)
|
||||
}
|
||||
if tc.method == "RunSlowly" {
|
||||
f.hang <- 1 // release the HTTP handler
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPICallDialFailure(t *testing.T) {
|
||||
// See what happens if the API host is unresponsive.
|
||||
// This should time out quickly, not hang forever.
|
||||
_, c, cleanup := setup()
|
||||
defer cleanup()
|
||||
// Reset the URL to the production address so that dialing fails.
|
||||
c.apiURL = apiURL()
|
||||
|
||||
start := time.Now()
|
||||
err := Call(toContext(c), "foo", "bar", &basepb.VoidProto{}, &basepb.VoidProto{})
|
||||
const max = 1 * time.Second
|
||||
if taken := time.Since(start); taken > max {
|
||||
t.Errorf("Dial hang took too long: %v > %v", taken, max)
|
||||
}
|
||||
if err == nil {
|
||||
t.Error("Call did not fail")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDelayedLogFlushing(t *testing.T) {
|
||||
f, c, cleanup := setup()
|
||||
defer cleanup()
|
||||
|
||||
http.HandleFunc("/quick_log", func(w http.ResponseWriter, r *http.Request) {
|
||||
logC := WithContext(netcontext.Background(), r)
|
||||
fromContext(logC).apiURL = c.apiURL // Otherwise it will try to use the default URL.
|
||||
Logf(logC, 1, "It's a lovely day.")
|
||||
w.WriteHeader(200)
|
||||
w.Write(make([]byte, 100<<10)) // write 100 KB to force HTTP flush
|
||||
})
|
||||
|
||||
r := &http.Request{
|
||||
Method: "GET",
|
||||
URL: &url.URL{
|
||||
Scheme: "http",
|
||||
Path: "/quick_log",
|
||||
},
|
||||
Header: c.req.Header,
|
||||
Body: ioutil.NopCloser(bytes.NewReader(nil)),
|
||||
}
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
// Check that log flushing does not hold up the HTTP response.
|
||||
start := time.Now()
|
||||
handleHTTP(w, r)
|
||||
if d := time.Since(start); d > 10*time.Millisecond {
|
||||
t.Errorf("handleHTTP took %v, want under 10ms", d)
|
||||
}
|
||||
const hdr = "X-AppEngine-Log-Flush-Count"
|
||||
if h := w.HeaderMap.Get(hdr); h != "1" {
|
||||
t.Errorf("%s header = %q, want %q", hdr, h, "1")
|
||||
}
|
||||
if f := atomic.LoadInt32(&f.LogFlushes); f != 0 {
|
||||
t.Errorf("After HTTP response: f.LogFlushes = %d, want 0", f)
|
||||
}
|
||||
|
||||
// Check that the log flush eventually comes in.
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
if f := atomic.LoadInt32(&f.LogFlushes); f != 1 {
|
||||
t.Errorf("After 100ms: f.LogFlushes = %d, want 1", f)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoteAddr(t *testing.T) {
|
||||
var addr string
|
||||
http.HandleFunc("/remote_addr", func(w http.ResponseWriter, r *http.Request) {
|
||||
addr = r.RemoteAddr
|
||||
})
|
||||
|
||||
testCases := []struct {
|
||||
headers http.Header
|
||||
addr string
|
||||
}{
|
||||
{http.Header{"X-Appengine-User-Ip": []string{"10.5.2.1"}}, "10.5.2.1:80"},
|
||||
{http.Header{"X-Appengine-Remote-Addr": []string{"1.2.3.4"}}, "1.2.3.4:80"},
|
||||
{http.Header{"X-Appengine-Remote-Addr": []string{"1.2.3.4:8080"}}, "1.2.3.4:8080"},
|
||||
{
|
||||
http.Header{"X-Appengine-Remote-Addr": []string{"2401:fa00:9:1:7646:a0ff:fe90:ca66"}},
|
||||
"[2401:fa00:9:1:7646:a0ff:fe90:ca66]:80",
|
||||
},
|
||||
{
|
||||
http.Header{"X-Appengine-Remote-Addr": []string{"[::1]:http"}},
|
||||
"[::1]:http",
|
||||
},
|
||||
{http.Header{}, "127.0.0.1:80"},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
r := &http.Request{
|
||||
Method: "GET",
|
||||
URL: &url.URL{Scheme: "http", Path: "/remote_addr"},
|
||||
Header: tc.headers,
|
||||
Body: ioutil.NopCloser(bytes.NewReader(nil)),
|
||||
}
|
||||
handleHTTP(httptest.NewRecorder(), r)
|
||||
if addr != tc.addr {
|
||||
t.Errorf("Header %v, got %q, want %q", tc.headers, addr, tc.addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPanickingHandler(t *testing.T) {
|
||||
http.HandleFunc("/panic", func(http.ResponseWriter, *http.Request) {
|
||||
panic("whoops!")
|
||||
})
|
||||
r := &http.Request{
|
||||
Method: "GET",
|
||||
URL: &url.URL{Scheme: "http", Path: "/panic"},
|
||||
Body: ioutil.NopCloser(bytes.NewReader(nil)),
|
||||
}
|
||||
rec := httptest.NewRecorder()
|
||||
handleHTTP(rec, r)
|
||||
if rec.Code != 500 {
|
||||
t.Errorf("Panicking handler returned HTTP %d, want HTTP %d", rec.Code, 500)
|
||||
}
|
||||
}
|
||||
|
||||
var raceDetector = false
|
||||
|
||||
func TestAPICallAllocations(t *testing.T) {
|
||||
if raceDetector {
|
||||
t.Skip("not running under race detector")
|
||||
}
|
||||
|
||||
// Run the test API server in a subprocess so we aren't counting its allocations.
|
||||
u, cleanup := launchHelperProcess(t)
|
||||
defer cleanup()
|
||||
c := &context{
|
||||
req: &http.Request{
|
||||
Header: http.Header{
|
||||
ticketHeader: []string{"s3cr3t"},
|
||||
dapperHeader: []string{"trace-001"},
|
||||
},
|
||||
},
|
||||
apiURL: u,
|
||||
}
|
||||
|
||||
req := &basepb.StringProto{
|
||||
Value: proto.String("Doctor Who"),
|
||||
}
|
||||
res := &basepb.StringProto{}
|
||||
var apiErr error
|
||||
avg := testing.AllocsPerRun(100, func() {
|
||||
ctx, _ := netcontext.WithTimeout(toContext(c), 100*time.Millisecond)
|
||||
if err := Call(ctx, "actordb", "LookupActor", req, res); err != nil && apiErr == nil {
|
||||
apiErr = err // get the first error only
|
||||
}
|
||||
})
|
||||
if apiErr != nil {
|
||||
t.Errorf("API call failed: %v", apiErr)
|
||||
}
|
||||
|
||||
// Lots of room for improvement...
|
||||
// TODO(djd): Reduce maximum to 85 once the App Engine SDK is based on 1.6.
|
||||
const min, max float64 = 70, 90
|
||||
if avg < min || max < avg {
|
||||
t.Errorf("Allocations per API call = %g, want in [%g,%g]", avg, min, max)
|
||||
}
|
||||
}
|
||||
|
||||
func launchHelperProcess(t *testing.T) (apiURL *url.URL, cleanup func()) {
|
||||
cmd := exec.Command(os.Args[0], "-test.run=TestHelperProcess")
|
||||
cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("StdinPipe: %v", err)
|
||||
}
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("StdoutPipe: %v", err)
|
||||
}
|
||||
if err := cmd.Start(); err != nil {
|
||||
t.Fatalf("Starting helper process: %v", err)
|
||||
}
|
||||
|
||||
scan := bufio.NewScanner(stdout)
|
||||
var u *url.URL
|
||||
for scan.Scan() {
|
||||
line := scan.Text()
|
||||
if hp := strings.TrimPrefix(line, helperProcessMagic); hp != line {
|
||||
var err error
|
||||
u, err = url.Parse(hp)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to parse %q: %v", hp, err)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if err := scan.Err(); err != nil {
|
||||
t.Fatalf("Scanning helper process stdout: %v", err)
|
||||
}
|
||||
if u == nil {
|
||||
t.Fatal("Helper process never reported")
|
||||
}
|
||||
|
||||
return u, func() {
|
||||
stdin.Close()
|
||||
if err := cmd.Wait(); err != nil {
|
||||
t.Errorf("Helper process did not exit cleanly: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const helperProcessMagic = "A lovely helper process is listening at "
|
||||
|
||||
// This isn't a real test. It's used as a helper process.
|
||||
func TestHelperProcess(*testing.T) {
|
||||
if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
|
||||
return
|
||||
}
|
||||
defer os.Exit(0)
|
||||
|
||||
f := &fakeAPIHandler{}
|
||||
srv := httptest.NewServer(f)
|
||||
defer srv.Close()
|
||||
fmt.Println(helperProcessMagic + srv.URL + apiPath)
|
||||
|
||||
// Wait for stdin to be closed.
|
||||
io.Copy(ioutil.Discard, os.Stdin)
|
||||
}
|
||||
|
||||
func TestBackgroundContext(t *testing.T) {
|
||||
environ := []struct {
|
||||
key, value string
|
||||
}{
|
||||
{"GAE_LONG_APP_ID", "my-app-id"},
|
||||
{"GAE_MINOR_VERSION", "067924799508853122"},
|
||||
{"GAE_MODULE_INSTANCE", "0"},
|
||||
{"GAE_MODULE_NAME", "default"},
|
||||
{"GAE_MODULE_VERSION", "20150612t184001"},
|
||||
}
|
||||
for _, v := range environ {
|
||||
old := os.Getenv(v.key)
|
||||
os.Setenv(v.key, v.value)
|
||||
v.value = old
|
||||
}
|
||||
defer func() { // Restore old environment after the test completes.
|
||||
for _, v := range environ {
|
||||
if v.value == "" {
|
||||
os.Unsetenv(v.key)
|
||||
continue
|
||||
}
|
||||
os.Setenv(v.key, v.value)
|
||||
}
|
||||
}()
|
||||
|
||||
ctx, key := fromContext(BackgroundContext()), "X-Magic-Ticket-Header"
|
||||
if g, w := ctx.req.Header.Get(key), "my-app-id/default.20150612t184001.0"; g != w {
|
||||
t.Errorf("%v = %q, want %q", key, g, w)
|
||||
}
|
||||
|
||||
// Check that using the background context doesn't panic.
|
||||
req := &basepb.StringProto{
|
||||
Value: proto.String("Doctor Who"),
|
||||
}
|
||||
res := &basepb.StringProto{}
|
||||
Call(BackgroundContext(), "actordb", "LookupActor", req, res) // expected to fail
|
||||
}
|
||||
34
vendor/google.golang.org/appengine/internal/app_id_test.go
generated
vendored
34
vendor/google.golang.org/appengine/internal/app_id_test.go
generated
vendored
@@ -1,34 +0,0 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAppIDParsing(t *testing.T) {
|
||||
testCases := []struct {
|
||||
in string
|
||||
partition, domain, displayID string
|
||||
}{
|
||||
{"simple-app-id", "", "", "simple-app-id"},
|
||||
{"domain.com:domain-app-id", "", "domain.com", "domain-app-id"},
|
||||
{"part~partition-app-id", "part", "", "partition-app-id"},
|
||||
{"part~domain.com:display", "part", "domain.com", "display"},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
part, dom, dis := parseFullAppID(tc.in)
|
||||
if part != tc.partition {
|
||||
t.Errorf("partition of %q: got %q, want %q", tc.in, part, tc.partition)
|
||||
}
|
||||
if dom != tc.domain {
|
||||
t.Errorf("domain of %q: got %q, want %q", tc.in, dom, tc.domain)
|
||||
}
|
||||
if dis != tc.displayID {
|
||||
t.Errorf("displayID of %q: got %q, want %q", tc.in, dis, tc.displayID)
|
||||
}
|
||||
}
|
||||
}
|
||||
33
vendor/google.golang.org/appengine/internal/base/api_base.proto
generated
vendored
33
vendor/google.golang.org/appengine/internal/base/api_base.proto
generated
vendored
@@ -1,33 +0,0 @@
|
||||
// Built-in base types for API calls. Primarily useful as return types.
|
||||
|
||||
syntax = "proto2";
|
||||
option go_package = "base";
|
||||
|
||||
package appengine.base;
|
||||
|
||||
message StringProto {
|
||||
required string value = 1;
|
||||
}
|
||||
|
||||
message Integer32Proto {
|
||||
required int32 value = 1;
|
||||
}
|
||||
|
||||
message Integer64Proto {
|
||||
required int64 value = 1;
|
||||
}
|
||||
|
||||
message BoolProto {
|
||||
required bool value = 1;
|
||||
}
|
||||
|
||||
message DoubleProto {
|
||||
required double value = 1;
|
||||
}
|
||||
|
||||
message BytesProto {
|
||||
required bytes value = 1 [ctype=CORD];
|
||||
}
|
||||
|
||||
message VoidProto {
|
||||
}
|
||||
541
vendor/google.golang.org/appengine/internal/datastore/datastore_v3.proto
generated
vendored
541
vendor/google.golang.org/appengine/internal/datastore/datastore_v3.proto
generated
vendored
@@ -1,541 +0,0 @@
|
||||
syntax = "proto2";
|
||||
option go_package = "datastore";
|
||||
|
||||
package appengine;
|
||||
|
||||
message Action{}
|
||||
|
||||
message PropertyValue {
|
||||
optional int64 int64Value = 1;
|
||||
optional bool booleanValue = 2;
|
||||
optional string stringValue = 3;
|
||||
optional double doubleValue = 4;
|
||||
|
||||
optional group PointValue = 5 {
|
||||
required double x = 6;
|
||||
required double y = 7;
|
||||
}
|
||||
|
||||
optional group UserValue = 8 {
|
||||
required string email = 9;
|
||||
required string auth_domain = 10;
|
||||
optional string nickname = 11;
|
||||
optional string federated_identity = 21;
|
||||
optional string federated_provider = 22;
|
||||
}
|
||||
|
||||
optional group ReferenceValue = 12 {
|
||||
required string app = 13;
|
||||
optional string name_space = 20;
|
||||
repeated group PathElement = 14 {
|
||||
required string type = 15;
|
||||
optional int64 id = 16;
|
||||
optional string name = 17;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
message Property {
|
||||
enum Meaning {
|
||||
NO_MEANING = 0;
|
||||
BLOB = 14;
|
||||
TEXT = 15;
|
||||
BYTESTRING = 16;
|
||||
|
||||
ATOM_CATEGORY = 1;
|
||||
ATOM_LINK = 2;
|
||||
ATOM_TITLE = 3;
|
||||
ATOM_CONTENT = 4;
|
||||
ATOM_SUMMARY = 5;
|
||||
ATOM_AUTHOR = 6;
|
||||
|
||||
GD_WHEN = 7;
|
||||
GD_EMAIL = 8;
|
||||
GEORSS_POINT = 9;
|
||||
GD_IM = 10;
|
||||
|
||||
GD_PHONENUMBER = 11;
|
||||
GD_POSTALADDRESS = 12;
|
||||
|
||||
GD_RATING = 13;
|
||||
|
||||
BLOBKEY = 17;
|
||||
ENTITY_PROTO = 19;
|
||||
|
||||
INDEX_VALUE = 18;
|
||||
};
|
||||
|
||||
optional Meaning meaning = 1 [default = NO_MEANING];
|
||||
optional string meaning_uri = 2;
|
||||
|
||||
required string name = 3;
|
||||
|
||||
required PropertyValue value = 5;
|
||||
|
||||
required bool multiple = 4;
|
||||
|
||||
optional bool searchable = 6 [default=false];
|
||||
|
||||
enum FtsTokenizationOption {
|
||||
HTML = 1;
|
||||
ATOM = 2;
|
||||
}
|
||||
|
||||
optional FtsTokenizationOption fts_tokenization_option = 8;
|
||||
|
||||
optional string locale = 9 [default = "en"];
|
||||
}
|
||||
|
||||
message Path {
|
||||
repeated group Element = 1 {
|
||||
required string type = 2;
|
||||
optional int64 id = 3;
|
||||
optional string name = 4;
|
||||
}
|
||||
}
|
||||
|
||||
message Reference {
|
||||
required string app = 13;
|
||||
optional string name_space = 20;
|
||||
required Path path = 14;
|
||||
}
|
||||
|
||||
message User {
|
||||
required string email = 1;
|
||||
required string auth_domain = 2;
|
||||
optional string nickname = 3;
|
||||
optional string federated_identity = 6;
|
||||
optional string federated_provider = 7;
|
||||
}
|
||||
|
||||
message EntityProto {
|
||||
required Reference key = 13;
|
||||
required Path entity_group = 16;
|
||||
optional User owner = 17;
|
||||
|
||||
enum Kind {
|
||||
GD_CONTACT = 1;
|
||||
GD_EVENT = 2;
|
||||
GD_MESSAGE = 3;
|
||||
}
|
||||
optional Kind kind = 4;
|
||||
optional string kind_uri = 5;
|
||||
|
||||
repeated Property property = 14;
|
||||
repeated Property raw_property = 15;
|
||||
|
||||
optional int32 rank = 18;
|
||||
}
|
||||
|
||||
message CompositeProperty {
|
||||
required int64 index_id = 1;
|
||||
repeated string value = 2;
|
||||
}
|
||||
|
||||
message Index {
|
||||
required string entity_type = 1;
|
||||
required bool ancestor = 5;
|
||||
repeated group Property = 2 {
|
||||
required string name = 3;
|
||||
enum Direction {
|
||||
ASCENDING = 1;
|
||||
DESCENDING = 2;
|
||||
}
|
||||
optional Direction direction = 4 [default = ASCENDING];
|
||||
}
|
||||
}
|
||||
|
||||
message CompositeIndex {
|
||||
required string app_id = 1;
|
||||
required int64 id = 2;
|
||||
required Index definition = 3;
|
||||
|
||||
enum State {
|
||||
WRITE_ONLY = 1;
|
||||
READ_WRITE = 2;
|
||||
DELETED = 3;
|
||||
ERROR = 4;
|
||||
}
|
||||
required State state = 4;
|
||||
|
||||
optional bool only_use_if_required = 6 [default = false];
|
||||
}
|
||||
|
||||
message IndexPostfix {
|
||||
message IndexValue {
|
||||
required string property_name = 1;
|
||||
required PropertyValue value = 2;
|
||||
}
|
||||
|
||||
repeated IndexValue index_value = 1;
|
||||
|
||||
optional Reference key = 2;
|
||||
|
||||
optional bool before = 3 [default=true];
|
||||
}
|
||||
|
||||
message IndexPosition {
|
||||
optional string key = 1;
|
||||
|
||||
optional bool before = 2 [default=true];
|
||||
}
|
||||
|
||||
message Snapshot {
|
||||
enum Status {
|
||||
INACTIVE = 0;
|
||||
ACTIVE = 1;
|
||||
}
|
||||
|
||||
required int64 ts = 1;
|
||||
}
|
||||
|
||||
message InternalHeader {
|
||||
optional string qos = 1;
|
||||
}
|
||||
|
||||
message Transaction {
|
||||
optional InternalHeader header = 4;
|
||||
required fixed64 handle = 1;
|
||||
required string app = 2;
|
||||
optional bool mark_changes = 3 [default = false];
|
||||
}
|
||||
|
||||
message Query {
|
||||
optional InternalHeader header = 39;
|
||||
|
||||
required string app = 1;
|
||||
optional string name_space = 29;
|
||||
|
||||
optional string kind = 3;
|
||||
optional Reference ancestor = 17;
|
||||
|
||||
repeated group Filter = 4 {
|
||||
enum Operator {
|
||||
LESS_THAN = 1;
|
||||
LESS_THAN_OR_EQUAL = 2;
|
||||
GREATER_THAN = 3;
|
||||
GREATER_THAN_OR_EQUAL = 4;
|
||||
EQUAL = 5;
|
||||
IN = 6;
|
||||
EXISTS = 7;
|
||||
}
|
||||
|
||||
required Operator op = 6;
|
||||
repeated Property property = 14;
|
||||
}
|
||||
|
||||
optional string search_query = 8;
|
||||
|
||||
repeated group Order = 9 {
|
||||
enum Direction {
|
||||
ASCENDING = 1;
|
||||
DESCENDING = 2;
|
||||
}
|
||||
|
||||
required string property = 10;
|
||||
optional Direction direction = 11 [default = ASCENDING];
|
||||
}
|
||||
|
||||
enum Hint {
|
||||
ORDER_FIRST = 1;
|
||||
ANCESTOR_FIRST = 2;
|
||||
FILTER_FIRST = 3;
|
||||
}
|
||||
optional Hint hint = 18;
|
||||
|
||||
optional int32 count = 23;
|
||||
|
||||
optional int32 offset = 12 [default = 0];
|
||||
|
||||
optional int32 limit = 16;
|
||||
|
||||
optional CompiledCursor compiled_cursor = 30;
|
||||
optional CompiledCursor end_compiled_cursor = 31;
|
||||
|
||||
repeated CompositeIndex composite_index = 19;
|
||||
|
||||
optional bool require_perfect_plan = 20 [default = false];
|
||||
|
||||
optional bool keys_only = 21 [default = false];
|
||||
|
||||
optional Transaction transaction = 22;
|
||||
|
||||
optional bool compile = 25 [default = false];
|
||||
|
||||
optional int64 failover_ms = 26;
|
||||
|
||||
optional bool strong = 32;
|
||||
|
||||
repeated string property_name = 33;
|
||||
|
||||
repeated string group_by_property_name = 34;
|
||||
|
||||
optional bool distinct = 24;
|
||||
|
||||
optional int64 min_safe_time_seconds = 35;
|
||||
|
||||
repeated string safe_replica_name = 36;
|
||||
|
||||
optional bool persist_offset = 37 [default=false];
|
||||
}
|
||||
|
||||
message CompiledQuery {
|
||||
required group PrimaryScan = 1 {
|
||||
optional string index_name = 2;
|
||||
|
||||
optional string start_key = 3;
|
||||
optional bool start_inclusive = 4;
|
||||
optional string end_key = 5;
|
||||
optional bool end_inclusive = 6;
|
||||
|
||||
repeated string start_postfix_value = 22;
|
||||
repeated string end_postfix_value = 23;
|
||||
|
||||
optional int64 end_unapplied_log_timestamp_us = 19;
|
||||
}
|
||||
|
||||
repeated group MergeJoinScan = 7 {
|
||||
required string index_name = 8;
|
||||
|
||||
repeated string prefix_value = 9;
|
||||
|
||||
optional bool value_prefix = 20 [default=false];
|
||||
}
|
||||
|
||||
optional Index index_def = 21;
|
||||
|
||||
optional int32 offset = 10 [default = 0];
|
||||
|
||||
optional int32 limit = 11;
|
||||
|
||||
required bool keys_only = 12;
|
||||
|
||||
repeated string property_name = 24;
|
||||
|
||||
optional int32 distinct_infix_size = 25;
|
||||
|
||||
optional group EntityFilter = 13 {
|
||||
optional bool distinct = 14 [default=false];
|
||||
|
||||
optional string kind = 17;
|
||||
optional Reference ancestor = 18;
|
||||
}
|
||||
}
|
||||
|
||||
message CompiledCursor {
|
||||
optional group Position = 2 {
|
||||
optional string start_key = 27;
|
||||
|
||||
repeated group IndexValue = 29 {
|
||||
optional string property = 30;
|
||||
required PropertyValue value = 31;
|
||||
}
|
||||
|
||||
optional Reference key = 32;
|
||||
|
||||
optional bool start_inclusive = 28 [default=true];
|
||||
}
|
||||
}
|
||||
|
||||
message Cursor {
|
||||
required fixed64 cursor = 1;
|
||||
|
||||
optional string app = 2;
|
||||
}
|
||||
|
||||
message Error {
|
||||
enum ErrorCode {
|
||||
BAD_REQUEST = 1;
|
||||
CONCURRENT_TRANSACTION = 2;
|
||||
INTERNAL_ERROR = 3;
|
||||
NEED_INDEX = 4;
|
||||
TIMEOUT = 5;
|
||||
PERMISSION_DENIED = 6;
|
||||
BIGTABLE_ERROR = 7;
|
||||
COMMITTED_BUT_STILL_APPLYING = 8;
|
||||
CAPABILITY_DISABLED = 9;
|
||||
TRY_ALTERNATE_BACKEND = 10;
|
||||
SAFE_TIME_TOO_OLD = 11;
|
||||
}
|
||||
}
|
||||
|
||||
message Cost {
|
||||
optional int32 index_writes = 1;
|
||||
optional int32 index_write_bytes = 2;
|
||||
optional int32 entity_writes = 3;
|
||||
optional int32 entity_write_bytes = 4;
|
||||
optional group CommitCost = 5 {
|
||||
optional int32 requested_entity_puts = 6;
|
||||
optional int32 requested_entity_deletes = 7;
|
||||
};
|
||||
optional int32 approximate_storage_delta = 8;
|
||||
optional int32 id_sequence_updates = 9;
|
||||
}
|
||||
|
||||
message GetRequest {
|
||||
optional InternalHeader header = 6;
|
||||
|
||||
repeated Reference key = 1;
|
||||
optional Transaction transaction = 2;
|
||||
|
||||
optional int64 failover_ms = 3;
|
||||
|
||||
optional bool strong = 4;
|
||||
|
||||
optional bool allow_deferred = 5 [default=false];
|
||||
}
|
||||
|
||||
message GetResponse {
|
||||
repeated group Entity = 1 {
|
||||
optional EntityProto entity = 2;
|
||||
optional Reference key = 4;
|
||||
|
||||
optional int64 version = 3;
|
||||
}
|
||||
|
||||
repeated Reference deferred = 5;
|
||||
|
||||
optional bool in_order = 6 [default=true];
|
||||
}
|
||||
|
||||
message PutRequest {
|
||||
optional InternalHeader header = 11;
|
||||
|
||||
repeated EntityProto entity = 1;
|
||||
optional Transaction transaction = 2;
|
||||
repeated CompositeIndex composite_index = 3;
|
||||
|
||||
optional bool trusted = 4 [default = false];
|
||||
|
||||
optional bool force = 7 [default = false];
|
||||
|
||||
optional bool mark_changes = 8 [default = false];
|
||||
repeated Snapshot snapshot = 9;
|
||||
|
||||
enum AutoIdPolicy {
|
||||
CURRENT = 0;
|
||||
SEQUENTIAL = 1;
|
||||
}
|
||||
optional AutoIdPolicy auto_id_policy = 10 [default = CURRENT];
|
||||
}
|
||||
|
||||
message PutResponse {
|
||||
repeated Reference key = 1;
|
||||
optional Cost cost = 2;
|
||||
repeated int64 version = 3;
|
||||
}
|
||||
|
||||
message TouchRequest {
|
||||
optional InternalHeader header = 10;
|
||||
|
||||
repeated Reference key = 1;
|
||||
repeated CompositeIndex composite_index = 2;
|
||||
optional bool force = 3 [default = false];
|
||||
repeated Snapshot snapshot = 9;
|
||||
}
|
||||
|
||||
message TouchResponse {
|
||||
optional Cost cost = 1;
|
||||
}
|
||||
|
||||
message DeleteRequest {
|
||||
optional InternalHeader header = 10;
|
||||
|
||||
repeated Reference key = 6;
|
||||
optional Transaction transaction = 5;
|
||||
|
||||
optional bool trusted = 4 [default = false];
|
||||
|
||||
optional bool force = 7 [default = false];
|
||||
|
||||
optional bool mark_changes = 8 [default = false];
|
||||
repeated Snapshot snapshot = 9;
|
||||
}
|
||||
|
||||
message DeleteResponse {
|
||||
optional Cost cost = 1;
|
||||
repeated int64 version = 3;
|
||||
}
|
||||
|
||||
message NextRequest {
|
||||
optional InternalHeader header = 5;
|
||||
|
||||
required Cursor cursor = 1;
|
||||
optional int32 count = 2;
|
||||
|
||||
optional int32 offset = 4 [default = 0];
|
||||
|
||||
optional bool compile = 3 [default = false];
|
||||
}
|
||||
|
||||
message QueryResult {
|
||||
optional Cursor cursor = 1;
|
||||
|
||||
repeated EntityProto result = 2;
|
||||
|
||||
optional int32 skipped_results = 7;
|
||||
|
||||
required bool more_results = 3;
|
||||
|
||||
optional bool keys_only = 4;
|
||||
|
||||
optional bool index_only = 9;
|
||||
|
||||
optional bool small_ops = 10;
|
||||
|
||||
optional CompiledQuery compiled_query = 5;
|
||||
|
||||
optional CompiledCursor compiled_cursor = 6;
|
||||
|
||||
repeated CompositeIndex index = 8;
|
||||
|
||||
repeated int64 version = 11;
|
||||
}
|
||||
|
||||
message AllocateIdsRequest {
|
||||
optional InternalHeader header = 4;
|
||||
|
||||
optional Reference model_key = 1;
|
||||
|
||||
optional int64 size = 2;
|
||||
|
||||
optional int64 max = 3;
|
||||
|
||||
repeated Reference reserve = 5;
|
||||
}
|
||||
|
||||
message AllocateIdsResponse {
|
||||
required int64 start = 1;
|
||||
required int64 end = 2;
|
||||
optional Cost cost = 3;
|
||||
}
|
||||
|
||||
message CompositeIndices {
|
||||
repeated CompositeIndex index = 1;
|
||||
}
|
||||
|
||||
message AddActionsRequest {
|
||||
optional InternalHeader header = 3;
|
||||
|
||||
required Transaction transaction = 1;
|
||||
repeated Action action = 2;
|
||||
}
|
||||
|
||||
message AddActionsResponse {
|
||||
}
|
||||
|
||||
message BeginTransactionRequest {
|
||||
optional InternalHeader header = 3;
|
||||
|
||||
required string app = 1;
|
||||
optional bool allow_multiple_eg = 2 [default = false];
|
||||
}
|
||||
|
||||
message CommitResponse {
|
||||
optional Cost cost = 1;
|
||||
|
||||
repeated group Version = 3 {
|
||||
required Reference root_entity_key = 4;
|
||||
required int64 version = 5;
|
||||
}
|
||||
}
|
||||
58
vendor/google.golang.org/appengine/internal/internal_test.go
generated
vendored
58
vendor/google.golang.org/appengine/internal/internal_test.go
generated
vendored
@@ -1,58 +0,0 @@
|
||||
// Copyright 2014 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestInstallingHealthChecker(t *testing.T) {
|
||||
try := func(desc string, mux *http.ServeMux, wantCode int, wantBody string) {
|
||||
installHealthChecker(mux)
|
||||
srv := httptest.NewServer(mux)
|
||||
defer srv.Close()
|
||||
|
||||
resp, err := http.Get(srv.URL + "/_ah/health")
|
||||
if err != nil {
|
||||
t.Errorf("%s: http.Get: %v", desc, err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Errorf("%s: reading body: %v", desc, err)
|
||||
return
|
||||
}
|
||||
|
||||
if resp.StatusCode != wantCode {
|
||||
t.Errorf("%s: got HTTP %d, want %d", desc, resp.StatusCode, wantCode)
|
||||
return
|
||||
}
|
||||
if wantBody != "" && string(body) != wantBody {
|
||||
t.Errorf("%s: got HTTP body %q, want %q", desc, body, wantBody)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// If there's no handlers, or only a root handler, a health checker should be installed.
|
||||
try("empty mux", http.NewServeMux(), 200, "ok")
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
io.WriteString(w, "root handler")
|
||||
})
|
||||
try("mux with root handler", mux, 200, "ok")
|
||||
|
||||
// If there's a custom health check handler, one should not be installed.
|
||||
mux = http.NewServeMux()
|
||||
mux.HandleFunc("/_ah/health", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(418)
|
||||
io.WriteString(w, "I'm short and stout!")
|
||||
})
|
||||
try("mux with custom health checker", mux, 418, "I'm short and stout!")
|
||||
}
|
||||
150
vendor/google.golang.org/appengine/internal/log/log_service.proto
generated
vendored
150
vendor/google.golang.org/appengine/internal/log/log_service.proto
generated
vendored
@@ -1,150 +0,0 @@
|
||||
syntax = "proto2";
|
||||
option go_package = "log";
|
||||
|
||||
package appengine;
|
||||
|
||||
message LogServiceError {
|
||||
enum ErrorCode {
|
||||
OK = 0;
|
||||
INVALID_REQUEST = 1;
|
||||
STORAGE_ERROR = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message UserAppLogLine {
|
||||
required int64 timestamp_usec = 1;
|
||||
required int64 level = 2;
|
||||
required string message = 3;
|
||||
}
|
||||
|
||||
message UserAppLogGroup {
|
||||
repeated UserAppLogLine log_line = 2;
|
||||
}
|
||||
|
||||
message FlushRequest {
|
||||
optional bytes logs = 1;
|
||||
}
|
||||
|
||||
message SetStatusRequest {
|
||||
required string status = 1;
|
||||
}
|
||||
|
||||
|
||||
message LogOffset {
|
||||
optional bytes request_id = 1;
|
||||
}
|
||||
|
||||
message LogLine {
|
||||
required int64 time = 1;
|
||||
required int32 level = 2;
|
||||
required string log_message = 3;
|
||||
}
|
||||
|
||||
message RequestLog {
|
||||
required string app_id = 1;
|
||||
optional string module_id = 37 [default="default"];
|
||||
required string version_id = 2;
|
||||
required bytes request_id = 3;
|
||||
optional LogOffset offset = 35;
|
||||
required string ip = 4;
|
||||
optional string nickname = 5;
|
||||
required int64 start_time = 6;
|
||||
required int64 end_time = 7;
|
||||
required int64 latency = 8;
|
||||
required int64 mcycles = 9;
|
||||
required string method = 10;
|
||||
required string resource = 11;
|
||||
required string http_version = 12;
|
||||
required int32 status = 13;
|
||||
required int64 response_size = 14;
|
||||
optional string referrer = 15;
|
||||
optional string user_agent = 16;
|
||||
required string url_map_entry = 17;
|
||||
required string combined = 18;
|
||||
optional int64 api_mcycles = 19;
|
||||
optional string host = 20;
|
||||
optional double cost = 21;
|
||||
|
||||
optional string task_queue_name = 22;
|
||||
optional string task_name = 23;
|
||||
|
||||
optional bool was_loading_request = 24;
|
||||
optional int64 pending_time = 25;
|
||||
optional int32 replica_index = 26 [default = -1];
|
||||
optional bool finished = 27 [default = true];
|
||||
optional bytes clone_key = 28;
|
||||
|
||||
repeated LogLine line = 29;
|
||||
|
||||
optional bool lines_incomplete = 36;
|
||||
optional bytes app_engine_release = 38;
|
||||
|
||||
optional int32 exit_reason = 30;
|
||||
optional bool was_throttled_for_time = 31;
|
||||
optional bool was_throttled_for_requests = 32;
|
||||
optional int64 throttled_time = 33;
|
||||
|
||||
optional bytes server_name = 34;
|
||||
}
|
||||
|
||||
message LogModuleVersion {
|
||||
optional string module_id = 1 [default="default"];
|
||||
optional string version_id = 2;
|
||||
}
|
||||
|
||||
message LogReadRequest {
|
||||
required string app_id = 1;
|
||||
repeated string version_id = 2;
|
||||
repeated LogModuleVersion module_version = 19;
|
||||
|
||||
optional int64 start_time = 3;
|
||||
optional int64 end_time = 4;
|
||||
optional LogOffset offset = 5;
|
||||
repeated bytes request_id = 6;
|
||||
|
||||
optional int32 minimum_log_level = 7;
|
||||
optional bool include_incomplete = 8;
|
||||
optional int64 count = 9;
|
||||
|
||||
optional string combined_log_regex = 14;
|
||||
optional string host_regex = 15;
|
||||
optional int32 replica_index = 16;
|
||||
|
||||
optional bool include_app_logs = 10;
|
||||
optional int32 app_logs_per_request = 17;
|
||||
optional bool include_host = 11;
|
||||
optional bool include_all = 12;
|
||||
optional bool cache_iterator = 13;
|
||||
optional int32 num_shards = 18;
|
||||
}
|
||||
|
||||
message LogReadResponse {
|
||||
repeated RequestLog log = 1;
|
||||
optional LogOffset offset = 2;
|
||||
optional int64 last_end_time = 3;
|
||||
}
|
||||
|
||||
message LogUsageRecord {
|
||||
optional string version_id = 1;
|
||||
optional int32 start_time = 2;
|
||||
optional int32 end_time = 3;
|
||||
optional int64 count = 4;
|
||||
optional int64 total_size = 5;
|
||||
optional int32 records = 6;
|
||||
}
|
||||
|
||||
message LogUsageRequest {
|
||||
required string app_id = 1;
|
||||
repeated string version_id = 2;
|
||||
optional int32 start_time = 3;
|
||||
optional int32 end_time = 4;
|
||||
optional uint32 resolution_hours = 5 [default = 1];
|
||||
optional bool combine_versions = 6;
|
||||
optional int32 usage_version = 7;
|
||||
optional bool versions_only = 8;
|
||||
}
|
||||
|
||||
message LogUsageResponse {
|
||||
repeated LogUsageRecord usage = 1;
|
||||
optional LogUsageRecord summary = 2;
|
||||
}
|
||||
58
vendor/google.golang.org/appengine/internal/net_test.go
generated
vendored
58
vendor/google.golang.org/appengine/internal/net_test.go
generated
vendored
@@ -1,58 +0,0 @@
|
||||
// Copyright 2014 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
netcontext "golang.org/x/net/context"
|
||||
|
||||
basepb "google.golang.org/appengine/internal/base"
|
||||
)
|
||||
|
||||
func TestDialLimit(t *testing.T) {
|
||||
// Fill up semaphore with false acquisitions to permit only two TCP connections at a time.
|
||||
// We don't replace limitSem because that results in a data race when net/http lazily closes connections.
|
||||
nFake := cap(limitSem) - 2
|
||||
for i := 0; i < nFake; i++ {
|
||||
limitSem <- 1
|
||||
}
|
||||
defer func() {
|
||||
for i := 0; i < nFake; i++ {
|
||||
<-limitSem
|
||||
}
|
||||
}()
|
||||
|
||||
f, c, cleanup := setup() // setup is in api_test.go
|
||||
defer cleanup()
|
||||
f.hang = make(chan int)
|
||||
|
||||
// If we make two RunSlowly RPCs (which will wait for f.hang to be strobed),
|
||||
// then the simple Non200 RPC should hang.
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
for i := 0; i < 2; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
Call(toContext(c), "errors", "RunSlowly", &basepb.VoidProto{}, &basepb.VoidProto{})
|
||||
}()
|
||||
}
|
||||
time.Sleep(50 * time.Millisecond) // let those two RPCs start
|
||||
|
||||
ctx, _ := netcontext.WithTimeout(toContext(c), 50*time.Millisecond)
|
||||
err := Call(ctx, "errors", "Non200", &basepb.VoidProto{}, &basepb.VoidProto{})
|
||||
if err != errTimeout {
|
||||
t.Errorf("Non200 RPC returned with err %v, want errTimeout", err)
|
||||
}
|
||||
|
||||
// Drain the two RunSlowly calls.
|
||||
f.hang <- 1
|
||||
f.hang <- 1
|
||||
wg.Wait()
|
||||
}
|
||||
40
vendor/google.golang.org/appengine/internal/regen.sh
generated
vendored
40
vendor/google.golang.org/appengine/internal/regen.sh
generated
vendored
@@ -1,40 +0,0 @@
|
||||
#!/bin/bash -e
|
||||
#
|
||||
# This script rebuilds the generated code for the protocol buffers.
|
||||
# To run this you will need protoc and goprotobuf installed;
|
||||
# see https://github.com/golang/protobuf for instructions.
|
||||
|
||||
PKG=google.golang.org/appengine
|
||||
|
||||
function die() {
|
||||
echo 1>&2 $*
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Sanity check that the right tools are accessible.
|
||||
for tool in go protoc protoc-gen-go; do
|
||||
q=$(which $tool) || die "didn't find $tool"
|
||||
echo 1>&2 "$tool: $q"
|
||||
done
|
||||
|
||||
echo -n 1>&2 "finding package dir... "
|
||||
pkgdir=$(go list -f '{{.Dir}}' $PKG)
|
||||
echo 1>&2 $pkgdir
|
||||
base=$(echo $pkgdir | sed "s,/$PKG\$,,")
|
||||
echo 1>&2 "base: $base"
|
||||
cd $base
|
||||
|
||||
# Run protoc once per package.
|
||||
for dir in $(find $PKG/internal -name '*.proto' | xargs dirname | sort | uniq); do
|
||||
echo 1>&2 "* $dir"
|
||||
protoc --go_out=. $dir/*.proto
|
||||
done
|
||||
|
||||
for f in $(find $PKG/internal -name '*.pb.go'); do
|
||||
# Remove proto.RegisterEnum calls.
|
||||
# These cause duplicate registration panics when these packages
|
||||
# are used on classic App Engine. proto.RegisterEnum only affects
|
||||
# parsing the text format; we don't care about that.
|
||||
# https://code.google.com/p/googleappengine/issues/detail?id=11670#c17
|
||||
sed -i '/proto.RegisterEnum/d' $f
|
||||
done
|
||||
44
vendor/google.golang.org/appengine/internal/remote_api/remote_api.proto
generated
vendored
44
vendor/google.golang.org/appengine/internal/remote_api/remote_api.proto
generated
vendored
@@ -1,44 +0,0 @@
|
||||
syntax = "proto2";
|
||||
option go_package = "remote_api";
|
||||
|
||||
package remote_api;
|
||||
|
||||
message Request {
|
||||
required string service_name = 2;
|
||||
required string method = 3;
|
||||
required bytes request = 4;
|
||||
optional string request_id = 5;
|
||||
}
|
||||
|
||||
message ApplicationError {
|
||||
required int32 code = 1;
|
||||
required string detail = 2;
|
||||
}
|
||||
|
||||
message RpcError {
|
||||
enum ErrorCode {
|
||||
UNKNOWN = 0;
|
||||
CALL_NOT_FOUND = 1;
|
||||
PARSE_ERROR = 2;
|
||||
SECURITY_VIOLATION = 3;
|
||||
OVER_QUOTA = 4;
|
||||
REQUEST_TOO_LARGE = 5;
|
||||
CAPABILITY_DISABLED = 6;
|
||||
FEATURE_DISABLED = 7;
|
||||
BAD_REQUEST = 8;
|
||||
RESPONSE_TOO_LARGE = 9;
|
||||
CANCELLED = 10;
|
||||
REPLAY_ERROR = 11;
|
||||
DEADLINE_EXCEEDED = 12;
|
||||
}
|
||||
required int32 code = 1;
|
||||
optional string detail = 2;
|
||||
}
|
||||
|
||||
message Response {
|
||||
optional bytes response = 1;
|
||||
optional bytes exception = 2;
|
||||
optional ApplicationError application_error = 3;
|
||||
optional bytes java_exception = 4;
|
||||
optional RpcError rpc_error = 5;
|
||||
}
|
||||
64
vendor/google.golang.org/appengine/internal/urlfetch/urlfetch_service.proto
generated
vendored
64
vendor/google.golang.org/appengine/internal/urlfetch/urlfetch_service.proto
generated
vendored
@@ -1,64 +0,0 @@
|
||||
syntax = "proto2";
|
||||
option go_package = "urlfetch";
|
||||
|
||||
package appengine;
|
||||
|
||||
message URLFetchServiceError {
|
||||
enum ErrorCode {
|
||||
OK = 0;
|
||||
INVALID_URL = 1;
|
||||
FETCH_ERROR = 2;
|
||||
UNSPECIFIED_ERROR = 3;
|
||||
RESPONSE_TOO_LARGE = 4;
|
||||
DEADLINE_EXCEEDED = 5;
|
||||
SSL_CERTIFICATE_ERROR = 6;
|
||||
DNS_ERROR = 7;
|
||||
CLOSED = 8;
|
||||
INTERNAL_TRANSIENT_ERROR = 9;
|
||||
TOO_MANY_REDIRECTS = 10;
|
||||
MALFORMED_REPLY = 11;
|
||||
CONNECTION_ERROR = 12;
|
||||
}
|
||||
}
|
||||
|
||||
message URLFetchRequest {
|
||||
enum RequestMethod {
|
||||
GET = 1;
|
||||
POST = 2;
|
||||
HEAD = 3;
|
||||
PUT = 4;
|
||||
DELETE = 5;
|
||||
PATCH = 6;
|
||||
}
|
||||
required RequestMethod Method = 1;
|
||||
required string Url = 2;
|
||||
repeated group Header = 3 {
|
||||
required string Key = 4;
|
||||
required string Value = 5;
|
||||
}
|
||||
optional bytes Payload = 6 [ctype=CORD];
|
||||
|
||||
optional bool FollowRedirects = 7 [default=true];
|
||||
|
||||
optional double Deadline = 8;
|
||||
|
||||
optional bool MustValidateServerCertificate = 9 [default=true];
|
||||
}
|
||||
|
||||
message URLFetchResponse {
|
||||
optional bytes Content = 1;
|
||||
required int32 StatusCode = 2;
|
||||
repeated group Header = 3 {
|
||||
required string Key = 4;
|
||||
required string Value = 5;
|
||||
}
|
||||
optional bool ContentWasTruncated = 6 [default=false];
|
||||
optional int64 ExternalBytesSent = 7;
|
||||
optional int64 ExternalBytesReceived = 8;
|
||||
|
||||
optional string FinalUrl = 9;
|
||||
|
||||
optional int64 ApiCpuMilliseconds = 10 [default=0];
|
||||
optional int64 ApiBytesSent = 11 [default=0];
|
||||
optional int64 ApiBytesReceived = 12 [default=0];
|
||||
}
|
||||
25
vendor/google.golang.org/appengine/namespace.go
generated
vendored
Normal file
25
vendor/google.golang.org/appengine/namespace.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2012 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package appengine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"google.golang.org/appengine/internal"
|
||||
)
|
||||
|
||||
// Namespace returns a replacement context that operates within the given namespace.
|
||||
func Namespace(c context.Context, namespace string) (context.Context, error) {
|
||||
if !validNamespace.MatchString(namespace) {
|
||||
return nil, fmt.Errorf("appengine: namespace %q does not match /%s/", namespace, validNamespace)
|
||||
}
|
||||
return internal.NamespacedContext(c, namespace), nil
|
||||
}
|
||||
|
||||
// validNamespace matches valid namespace names.
|
||||
var validNamespace = regexp.MustCompile(`^[0-9A-Za-z._-]{0,100}$`)
|
||||
20
vendor/google.golang.org/appengine/timeout.go
generated
vendored
Normal file
20
vendor/google.golang.org/appengine/timeout.go
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright 2013 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package appengine
|
||||
|
||||
import "golang.org/x/net/context"
|
||||
|
||||
// IsTimeoutError reports whether err is a timeout error.
|
||||
func IsTimeoutError(err error) bool {
|
||||
if err == context.DeadlineExceeded {
|
||||
return true
|
||||
}
|
||||
if t, ok := err.(interface {
|
||||
IsTimeout() bool
|
||||
}); ok {
|
||||
return t.IsTimeout()
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user