internal.go (3152B)
1 // Copyright 2011 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 // Package internal provides support for package appengine. 6 // 7 // Programs should not use this package directly. Its API is not stable. 8 // Use packages appengine and appengine/* instead. 9 package internal 10 11 import ( 12 "fmt" 13 14 "github.com/golang/protobuf/proto" 15 16 remotepb "google.golang.org/appengine/internal/remote_api" 17 ) 18 19 // errorCodeMaps is a map of service name to the error code map for the service. 20 var errorCodeMaps = make(map[string]map[int32]string) 21 22 // RegisterErrorCodeMap is called from API implementations to register their 23 // error code map. This should only be called from init functions. 24 func RegisterErrorCodeMap(service string, m map[int32]string) { 25 errorCodeMaps[service] = m 26 } 27 28 type timeoutCodeKey struct { 29 service string 30 code int32 31 } 32 33 // timeoutCodes is the set of service+code pairs that represent timeouts. 34 var timeoutCodes = make(map[timeoutCodeKey]bool) 35 36 func RegisterTimeoutErrorCode(service string, code int32) { 37 timeoutCodes[timeoutCodeKey{service, code}] = true 38 } 39 40 // APIError is the type returned by appengine.Context's Call method 41 // when an API call fails in an API-specific way. This may be, for instance, 42 // a taskqueue API call failing with TaskQueueServiceError::UNKNOWN_QUEUE. 43 type APIError struct { 44 Service string 45 Detail string 46 Code int32 // API-specific error code 47 } 48 49 func (e *APIError) Error() string { 50 if e.Code == 0 { 51 if e.Detail == "" { 52 return "APIError <empty>" 53 } 54 return e.Detail 55 } 56 s := fmt.Sprintf("API error %d", e.Code) 57 if m, ok := errorCodeMaps[e.Service]; ok { 58 s += " (" + e.Service + ": " + m[e.Code] + ")" 59 } else { 60 // Shouldn't happen, but provide a bit more detail if it does. 61 s = e.Service + " " + s 62 } 63 if e.Detail != "" { 64 s += ": " + e.Detail 65 } 66 return s 67 } 68 69 func (e *APIError) IsTimeout() bool { 70 return timeoutCodes[timeoutCodeKey{e.Service, e.Code}] 71 } 72 73 // CallError is the type returned by appengine.Context's Call method when an 74 // API call fails in a generic way, such as RpcError::CAPABILITY_DISABLED. 75 type CallError struct { 76 Detail string 77 Code int32 78 // TODO: Remove this if we get a distinguishable error code. 79 Timeout bool 80 } 81 82 func (e *CallError) Error() string { 83 var msg string 84 switch remotepb.RpcError_ErrorCode(e.Code) { 85 case remotepb.RpcError_UNKNOWN: 86 return e.Detail 87 case remotepb.RpcError_OVER_QUOTA: 88 msg = "Over quota" 89 case remotepb.RpcError_CAPABILITY_DISABLED: 90 msg = "Capability disabled" 91 case remotepb.RpcError_CANCELLED: 92 msg = "Canceled" 93 default: 94 msg = fmt.Sprintf("Call error %d", e.Code) 95 } 96 s := msg + ": " + e.Detail 97 if e.Timeout { 98 s += " (timeout)" 99 } 100 return s 101 } 102 103 func (e *CallError) IsTimeout() bool { 104 return e.Timeout 105 } 106 107 // NamespaceMods is a map from API service to a function that will mutate an RPC request to attach a namespace. 108 // The function should be prepared to be called on the same message more than once; it should only modify the 109 // RPC request the first time. 110 var NamespaceMods = make(map[string]func(m proto.Message, namespace string))