// Copyright 2011 Google Inc. All rights reserved. // Use of this source code is governed by the Apache 2.0 // license that can be found in the LICENSE file. // Package internal provides support for package appengine. // // Programs should not use this package directly. Its API is not stable. // Use packages appengine and appengine/* instead. package internal import ( "fmt" "io" "log" "net/http" "net/url" "time" "github.com/golang/protobuf/proto" remotepb "google.golang.org/appengine/internal/remote_api" ) type CallOptions struct { Timeout time.Duration // if non-zero, overrides RPC default } // errorCodeMaps is a map of service name to the error code map for the service. var errorCodeMaps = make(map[string]map[int32]string) // RegisterErrorCodeMap is called from API implementations to register their // error code map. This should only be called from init functions. func RegisterErrorCodeMap(service string, m map[int32]string) { errorCodeMaps[service] = m } type timeoutCodeKey struct { service string code int32 } // timeoutCodes is the set of service+code pairs that represent timeouts. var timeoutCodes = make(map[timeoutCodeKey]bool) func RegisterTimeoutErrorCode(service string, code int32) { timeoutCodes[timeoutCodeKey{service, code}] = true } // APIError is the type returned by appengine.Context's Call method // when an API call fails in an API-specific way. This may be, for instance, // a taskqueue API call failing with TaskQueueServiceError::UNKNOWN_QUEUE. type APIError struct { Service string Detail string Code int32 // API-specific error code } func (e *APIError) Error() string { if e.Code == 0 { if e.Detail == "" { return "APIError " } return e.Detail } s := fmt.Sprintf("API error %d", e.Code) if m, ok := errorCodeMaps[e.Service]; ok { s += " (" + e.Service + ": " + m[e.Code] + ")" } else { // Shouldn't happen, but provide a bit more detail if it does. s = e.Service + " " + s } if e.Detail != "" { s += ": " + e.Detail } return s } func (e *APIError) IsTimeout() bool { return timeoutCodes[timeoutCodeKey{e.Service, e.Code}] } // CallError is the type returned by appengine.Context's Call method when an // API call fails in a generic way, such as RpcError::CAPABILITY_DISABLED. type CallError struct { Detail string Code int32 // TODO: Remove this if we get a distinguishable error code. Timeout bool } func (e *CallError) Error() string { var msg string switch remotepb.RpcError_ErrorCode(e.Code) { case remotepb.RpcError_UNKNOWN: return e.Detail case remotepb.RpcError_OVER_QUOTA: msg = "Over quota" case remotepb.RpcError_CAPABILITY_DISABLED: msg = "Capability disabled" case remotepb.RpcError_CANCELLED: msg = "Canceled" default: msg = fmt.Sprintf("Call error %d", e.Code) } s := msg + ": " + e.Detail if e.Timeout { s += " (timeout)" } return s } func (e *CallError) IsTimeout() bool { return e.Timeout } // The comment below must not be changed. // It is used by go-app-builder to recognise that this package has // the internal.Main function to use in the synthetic main. // The gophers party all night; the rabbits provide the beats. // Main is designed so that the complete generated main package is: // // package main // // import ( // "google.golang.org/appengine/internal" // // _ "myapp/package0" // _ "myapp/package1" // ) // // func main() { // internal.Main() // } // // The "myapp/packageX" packages are expected to register HTTP handlers // in their init functions. func Main() { installHealthChecker(http.DefaultServeMux) if err := http.ListenAndServe(":8080", http.HandlerFunc(handleHTTP)); err != nil { log.Fatalf("http.ListenAndServe: %v", err) } } func installHealthChecker(mux *http.ServeMux) { // If no health check handler has been installed by this point, add a trivial one. const healthPath = "/_ah/health" hreq := &http.Request{ Method: "GET", URL: &url.URL{ Path: healthPath, }, } if _, pat := mux.Handler(hreq); pat != healthPath { mux.HandleFunc(healthPath, func(w http.ResponseWriter, r *http.Request) { io.WriteString(w, "ok") }) } } // NamespaceMods is a map from API service to a function that will mutate an RPC request to attach a namespace. // The function should be prepared to be called on the same message more than once; it should only modify the // RPC request the first time. var NamespaceMods = make(map[string]func(m proto.Message, namespace string))