internal.go

  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))