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. 4 5// +build appengine 6 7package internal 8 9import ( 10 "errors" 11 "fmt" 12 "net/http" 13 "time" 14 15 "appengine" 16 "appengine_internal" 17 basepb "appengine_internal/base" 18 19 "github.com/golang/protobuf/proto" 20 netcontext "golang.org/x/net/context" 21) 22 23var contextKey = "holds an appengine.Context" 24 25// fromContext returns the App Engine context or nil if ctx is not 26// derived from an App Engine context. 27func fromContext(ctx netcontext.Context) appengine.Context { 28 c, _ := ctx.Value(&contextKey).(appengine.Context) 29 return c 30} 31 32// This is only for classic App Engine adapters. 33func ClassicContextFromContext(ctx netcontext.Context) (appengine.Context, error) { 34 c := fromContext(ctx) 35 if c == nil { 36 return nil, errNotAppEngineContext 37 } 38 return c, nil 39} 40 41func withContext(parent netcontext.Context, c appengine.Context) netcontext.Context { 42 ctx := netcontext.WithValue(parent, &contextKey, c) 43 44 s := &basepb.StringProto{} 45 c.Call("__go__", "GetNamespace", &basepb.VoidProto{}, s, nil) 46 if ns := s.GetValue(); ns != "" { 47 ctx = NamespacedContext(ctx, ns) 48 } 49 50 return ctx 51} 52 53func IncomingHeaders(ctx netcontext.Context) http.Header { 54 if c := fromContext(ctx); c != nil { 55 if req, ok := c.Request().(*http.Request); ok { 56 return req.Header 57 } 58 } 59 return nil 60} 61 62func ReqContext(req *http.Request) netcontext.Context { 63 return WithContext(netcontext.Background(), req) 64} 65 66func WithContext(parent netcontext.Context, req *http.Request) netcontext.Context { 67 c := appengine.NewContext(req) 68 return withContext(parent, c) 69} 70 71type testingContext struct { 72 appengine.Context 73 74 req *http.Request 75} 76 77func (t *testingContext) FullyQualifiedAppID() string { return "dev~testcontext" } 78func (t *testingContext) Call(service, method string, _, _ appengine_internal.ProtoMessage, _ *appengine_internal.CallOptions) error { 79 if service == "__go__" && method == "GetNamespace" { 80 return nil 81 } 82 return fmt.Errorf("testingContext: unsupported Call") 83} 84func (t *testingContext) Request() interface{} { return t.req } 85 86func ContextForTesting(req *http.Request) netcontext.Context { 87 return withContext(netcontext.Background(), &testingContext{req: req}) 88} 89 90func 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 { 93 fn(in, ns) 94 } 95 } 96 97 if f, ctx, ok := callOverrideFromContext(ctx); ok { 98 return f(ctx, service, method, in, out) 99 } 100 101 // Handle already-done contexts quickly. 102 select { 103 case <-ctx.Done(): 104 return ctx.Err() 105 default: 106 } 107 108 c := fromContext(ctx) 109 if c == nil { 110 // Give a good error message rather than a panic lower down. 111 return errNotAppEngineContext 112 } 113 114 // Apply transaction modifications if we're in a transaction. 115 if t := transactionFromContext(ctx); t != nil { 116 if t.finished { 117 return errors.New("transaction context has expired") 118 } 119 applyTransaction(in, &t.transaction) 120 } 121 122 var opts *appengine_internal.CallOptions 123 if d, ok := ctx.Deadline(); ok { 124 opts = &appengine_internal.CallOptions{ 125 Timeout: d.Sub(time.Now()), 126 } 127 } 128 129 err := c.Call(service, method, in, out, opts) 130 switch v := err.(type) { 131 case *appengine_internal.APIError: 132 return &APIError{ 133 Service: v.Service, 134 Detail: v.Detail, 135 Code: v.Code, 136 } 137 case *appengine_internal.CallError: 138 return &CallError{ 139 Detail: v.Detail, 140 Code: v.Code, 141 Timeout: v.Timeout, 142 } 143 } 144 return err 145} 146 147func handleHTTP(w http.ResponseWriter, r *http.Request) { 148 panic("handleHTTP called; this should be impossible") 149} 150 151func logf(c appengine.Context, level int64, format string, args ...interface{}) { 152 var fn func(format string, args ...interface{}) 153 switch level { 154 case 0: 155 fn = c.Debugf 156 case 1: 157 fn = c.Infof 158 case 2: 159 fn = c.Warningf 160 case 3: 161 fn = c.Errorf 162 case 4: 163 fn = c.Criticalf 164 default: 165 // This shouldn't happen. 166 fn = c.Criticalf 167 } 168 fn(format, args...) 169} 170