1 // Copyright 2015 Google Inc. All rights reserved.
2 // Use of this source code is governed by the Apache 2.0
3 // license that can be found in the LICENSE file.
17 basepb "appengine_internal/base"
19 "github.com/golang/protobuf/proto"
20 netcontext "golang.org/x/net/context"
23 var contextKey = "holds an appengine.Context"
25 // fromContext returns the App Engine context or nil if ctx is not
26 // derived from an App Engine context.
27 func fromContext(ctx netcontext.Context) appengine.Context {
28 c, _ := ctx.Value(&contextKey).(appengine.Context)
32 // This is only for classic App Engine adapters.
33 func ClassicContextFromContext(ctx netcontext.Context) (appengine.Context, error) {
36 return nil, errNotAppEngineContext
41 func withContext(parent netcontext.Context, c appengine.Context) netcontext.Context {
42 ctx := netcontext.WithValue(parent, &contextKey, c)
44 s := &basepb.StringProto{}
45 c.Call("__go__", "GetNamespace", &basepb.VoidProto{}, s, nil)
46 if ns := s.GetValue(); ns != "" {
47 ctx = NamespacedContext(ctx, ns)
53 func IncomingHeaders(ctx netcontext.Context) http.Header {
54 if c := fromContext(ctx); c != nil {
55 if req, ok := c.Request().(*http.Request); ok {
62 func ReqContext(req *http.Request) netcontext.Context {
63 return WithContext(netcontext.Background(), req)
66 func WithContext(parent netcontext.Context, req *http.Request) netcontext.Context {
67 c := appengine.NewContext(req)
68 return withContext(parent, c)
71 type testingContext struct {
77 func (t *testingContext) FullyQualifiedAppID() string { return "dev~testcontext" }
78 func (t *testingContext) Call(service, method string, _, _ appengine_internal.ProtoMessage, _ *appengine_internal.CallOptions) error {
79 if service == "__go__" && method == "GetNamespace" {
82 return fmt.Errorf("testingContext: unsupported Call")
84 func (t *testingContext) Request() interface{} { return t.req }
86 func ContextForTesting(req *http.Request) netcontext.Context {
87 return withContext(netcontext.Background(), &testingContext{req: req})
90 func Call(ctx netcontext.Context, service, method string, in, out proto.Message) error {
91 if ns := NamespaceFromContext(ctx); ns != "" {
92 if fn, ok := NamespaceMods[service]; ok {
97 if f, ctx, ok := callOverrideFromContext(ctx); ok {
98 return f(ctx, service, method, in, out)
101 // Handle already-done contexts quickly.
108 c := fromContext(ctx)
110 // Give a good error message rather than a panic lower down.
111 return errNotAppEngineContext
114 // Apply transaction modifications if we're in a transaction.
115 if t := transactionFromContext(ctx); t != nil {
117 return errors.New("transaction context has expired")
119 applyTransaction(in, &t.transaction)
122 var opts *appengine_internal.CallOptions
123 if d, ok := ctx.Deadline(); ok {
124 opts = &appengine_internal.CallOptions{
125 Timeout: d.Sub(time.Now()),
129 err := c.Call(service, method, in, out, opts)
130 switch v := err.(type) {
131 case *appengine_internal.APIError:
137 case *appengine_internal.CallError:
147 func handleHTTP(w http.ResponseWriter, r *http.Request) {
148 panic("handleHTTP called; this should be impossible")
151 func logf(c appengine.Context, level int64, format string, args ...interface{}) {
152 var fn func(format string, args ...interface{})
165 // This shouldn't happen.