internal: give more useful panics when (mis)using context.Background (#68)
Instead of waiting for a nil return from fromContext to panic, I've
updated all callers to proactively return/panic with a common error
type. Panic is appropriate here, as it is a failed precondition of
values passed into the functions. It's not introducing any new panics,
just ones with more useful messages.
Addresses GoogleCloudPlatform/google-cloud-go#523
diff --git a/internal/api.go b/internal/api.go
index aba0f83..efee060 100644
--- a/internal/api.go
+++ b/internal/api.go
@@ -227,6 +227,8 @@
var contextKey = "holds a *context"
+// fromContext returns the App Engine context or nil if ctx is not
+// derived from an App Engine context.
func fromContext(ctx netcontext.Context) *context {
c, _ := ctx.Value(&contextKey).(*context)
return c
@@ -468,7 +470,7 @@
c := fromContext(ctx)
if c == nil {
// Give a good error message rather than a panic lower down.
- return errors.New("not an App Engine context")
+ return errNotAppEngineContext
}
// Apply transaction modifications if we're in a transaction.
diff --git a/internal/api_classic.go b/internal/api_classic.go
index 597f66e..952b6e6 100644
--- a/internal/api_classic.go
+++ b/internal/api_classic.go
@@ -22,14 +22,20 @@
var contextKey = "holds an appengine.Context"
+// fromContext returns the App Engine context or nil if ctx is not
+// derived from an App Engine context.
func fromContext(ctx netcontext.Context) appengine.Context {
c, _ := ctx.Value(&contextKey).(appengine.Context)
return c
}
// This is only for classic App Engine adapters.
-func ClassicContextFromContext(ctx netcontext.Context) appengine.Context {
- return fromContext(ctx)
+func ClassicContextFromContext(ctx netcontext.Context) (appengine.Context, error) {
+ c := fromContext(ctx)
+ if c == nil {
+ return nil, errNotAppEngineContext
+ }
+ return c, nil
}
func withContext(parent netcontext.Context, c appengine.Context) netcontext.Context {
@@ -98,7 +104,7 @@
c := fromContext(ctx)
if c == nil {
// Give a good error message rather than a panic lower down.
- return errors.New("not an App Engine context")
+ return errNotAppEngineContext
}
// Apply transaction modifications if we're in a transaction.
diff --git a/internal/api_common.go b/internal/api_common.go
index 8c3eece..e0c0b21 100644
--- a/internal/api_common.go
+++ b/internal/api_common.go
@@ -5,12 +5,15 @@
package internal
import (
+ "errors"
"os"
"github.com/golang/protobuf/proto"
netcontext "golang.org/x/net/context"
)
+var errNotAppEngineContext = errors.New("not an App Engine context")
+
type CallOverrideFunc func(ctx netcontext.Context, service, method string, in, out proto.Message) error
var callOverrideKey = "holds []CallOverrideFunc"
@@ -79,7 +82,11 @@
f(level, format, args...)
return
}
- logf(fromContext(ctx), level, format, args...)
+ c := fromContext(ctx)
+ if c == nil {
+ panic(errNotAppEngineContext)
+ }
+ logf(c, level, format, args...)
}
// NamespacedContext wraps a Context to support namespaces.
diff --git a/internal/identity_classic.go b/internal/identity_classic.go
index e6b9227..b59603f 100644
--- a/internal/identity_classic.go
+++ b/internal/identity_classic.go
@@ -13,15 +13,45 @@
)
func DefaultVersionHostname(ctx netcontext.Context) string {
- return appengine.DefaultVersionHostname(fromContext(ctx))
+ c := fromContext(ctx)
+ if c == nil {
+ panic(errNotAppEngineContext)
+ }
+ return appengine.DefaultVersionHostname(c)
}
-func RequestID(ctx netcontext.Context) string { return appengine.RequestID(fromContext(ctx)) }
-func Datacenter(_ netcontext.Context) string { return appengine.Datacenter() }
-func ServerSoftware() string { return appengine.ServerSoftware() }
-func ModuleName(ctx netcontext.Context) string { return appengine.ModuleName(fromContext(ctx)) }
-func VersionID(ctx netcontext.Context) string { return appengine.VersionID(fromContext(ctx)) }
-func InstanceID() string { return appengine.InstanceID() }
-func IsDevAppServer() bool { return appengine.IsDevAppServer() }
+func Datacenter(_ netcontext.Context) string { return appengine.Datacenter() }
+func ServerSoftware() string { return appengine.ServerSoftware() }
+func InstanceID() string { return appengine.InstanceID() }
+func IsDevAppServer() bool { return appengine.IsDevAppServer() }
-func fullyQualifiedAppID(ctx netcontext.Context) string { return fromContext(ctx).FullyQualifiedAppID() }
+func RequestID(ctx netcontext.Context) string {
+ c := fromContext(ctx)
+ if c == nil {
+ panic(errNotAppEngineContext)
+ }
+ return appengine.RequestID(c)
+}
+
+func ModuleName(ctx netcontext.Context) string {
+ c := fromContext(ctx)
+ if c == nil {
+ panic(errNotAppEngineContext)
+ }
+ return appengine.ModuleName(c)
+}
+func VersionID(ctx netcontext.Context) string {
+ c := fromContext(ctx)
+ if c == nil {
+ panic(errNotAppEngineContext)
+ }
+ return appengine.VersionID(c)
+}
+
+func fullyQualifiedAppID(ctx netcontext.Context) string {
+ c := fromContext(ctx)
+ if c == nil {
+ panic(errNotAppEngineContext)
+ }
+ return c.FullyQualifiedAppID()
+}
diff --git a/internal/identity_vm.go b/internal/identity_vm.go
index ebe68b7..d5fa75b 100644
--- a/internal/identity_vm.go
+++ b/internal/identity_vm.go
@@ -23,7 +23,11 @@
)
func ctxHeaders(ctx netcontext.Context) http.Header {
- return fromContext(ctx).Request().Header
+ c := fromContext(ctx)
+ if c == nil {
+ return nil
+ }
+ return c.Request().Header
}
func DefaultVersionHostname(ctx netcontext.Context) string {
diff --git a/user/user_classic.go b/user/user_classic.go
index a747ef3..929e4a3 100644
--- a/user/user_classic.go
+++ b/user/user_classic.go
@@ -15,7 +15,11 @@
)
func Current(ctx context.Context) *User {
- u := user.Current(internal.ClassicContextFromContext(ctx))
+ c, err := internal.ClassicContextFromContext(ctx)
+ if err != nil {
+ panic(err)
+ }
+ u := user.Current(c)
if u == nil {
return nil
}