api_classic.go (4041B)
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 7 package internal 8 9 import ( 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 23 var 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. 27 func 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. 33 func 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 41 func 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 53 func 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 62 func ReqContext(req *http.Request) netcontext.Context { 63 return WithContext(netcontext.Background(), req) 64 } 65 66 func WithContext(parent netcontext.Context, req *http.Request) netcontext.Context { 67 c := appengine.NewContext(req) 68 return withContext(parent, c) 69 } 70 71 type testingContext struct { 72 appengine.Context 73 74 req *http.Request 75 } 76 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" { 80 return nil 81 } 82 return fmt.Errorf("testingContext: unsupported Call") 83 } 84 func (t *testingContext) Request() interface{} { return t.req } 85 86 func ContextForTesting(req *http.Request) netcontext.Context { 87 return withContext(netcontext.Background(), &testingContext{req: req}) 88 } 89 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 { 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 147 func handleHTTP(w http.ResponseWriter, r *http.Request) { 148 panic("handleHTTP called; this should be impossible") 149 } 150 151 func 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 }