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. 9package internal 10 11import ( 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. 20var 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. 24func RegisterErrorCodeMap(service string, m map[int32]string) { 25 errorCodeMaps[service] = m 26} 27 28type timeoutCodeKey struct { 29 service string 30 code int32 31} 32 33// timeoutCodes is the set of service+code pairs that represent timeouts. 34var timeoutCodes = make(map[timeoutCodeKey]bool) 35 36func 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. 43type APIError struct { 44 Service string 45 Detail string 46 Code int32 // API-specific error code 47} 48 49func (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 69func (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. 75type CallError struct { 76 Detail string 77 Code int32 78 // TODO: Remove this if we get a distinguishable error code. 79 Timeout bool 80} 81 82func (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 103func (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. 110var NamespaceMods = make(map[string]func(m proto.Message, namespace string)) 111