internal.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  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. // Package internal provides support for package appengine.
  5. //
  6. // Programs should not use this package directly. Its API is not stable.
  7. // Use packages appengine and appengine/* instead.
  8. package internal
  9. import (
  10. "fmt"
  11. "io"
  12. "log"
  13. "net/http"
  14. "net/url"
  15. "os"
  16. "github.com/golang/protobuf/proto"
  17. remotepb "google.golang.org/appengine/internal/remote_api"
  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. // RegisterErrorCodeMap is called from API implementations to register their
  22. // error code map. This should only be called from init functions.
  23. func RegisterErrorCodeMap(service string, m map[int32]string) {
  24. errorCodeMaps[service] = m
  25. }
  26. type timeoutCodeKey struct {
  27. service string
  28. code int32
  29. }
  30. // timeoutCodes is the set of service+code pairs that represent timeouts.
  31. var timeoutCodes = make(map[timeoutCodeKey]bool)
  32. func RegisterTimeoutErrorCode(service string, code int32) {
  33. timeoutCodes[timeoutCodeKey{service, code}] = true
  34. }
  35. // APIError is the type returned by appengine.Context's Call method
  36. // when an API call fails in an API-specific way. This may be, for instance,
  37. // a taskqueue API call failing with TaskQueueServiceError::UNKNOWN_QUEUE.
  38. type APIError struct {
  39. Service string
  40. Detail string
  41. Code int32 // API-specific error code
  42. }
  43. func (e *APIError) Error() string {
  44. if e.Code == 0 {
  45. if e.Detail == "" {
  46. return "APIError <empty>"
  47. }
  48. return e.Detail
  49. }
  50. s := fmt.Sprintf("API error %d", e.Code)
  51. if m, ok := errorCodeMaps[e.Service]; ok {
  52. s += " (" + e.Service + ": " + m[e.Code] + ")"
  53. } else {
  54. // Shouldn't happen, but provide a bit more detail if it does.
  55. s = e.Service + " " + s
  56. }
  57. if e.Detail != "" {
  58. s += ": " + e.Detail
  59. }
  60. return s
  61. }
  62. func (e *APIError) IsTimeout() bool {
  63. return timeoutCodes[timeoutCodeKey{e.Service, e.Code}]
  64. }
  65. // CallError is the type returned by appengine.Context's Call method when an
  66. // API call fails in a generic way, such as RpcError::CAPABILITY_DISABLED.
  67. type CallError struct {
  68. Detail string
  69. Code int32
  70. // TODO: Remove this if we get a distinguishable error code.
  71. Timeout bool
  72. }
  73. func (e *CallError) Error() string {
  74. var msg string
  75. switch remotepb.RpcError_ErrorCode(e.Code) {
  76. case remotepb.RpcError_UNKNOWN:
  77. return e.Detail
  78. case remotepb.RpcError_OVER_QUOTA:
  79. msg = "Over quota"
  80. case remotepb.RpcError_CAPABILITY_DISABLED:
  81. msg = "Capability disabled"
  82. case remotepb.RpcError_CANCELLED:
  83. msg = "Canceled"
  84. default:
  85. msg = fmt.Sprintf("Call error %d", e.Code)
  86. }
  87. s := msg + ": " + e.Detail
  88. if e.Timeout {
  89. s += " (timeout)"
  90. }
  91. return s
  92. }
  93. func (e *CallError) IsTimeout() bool {
  94. return e.Timeout
  95. }
  96. func Main() {
  97. installHealthChecker(http.DefaultServeMux)
  98. port := "8080"
  99. if s := os.Getenv("PORT"); s != "" {
  100. port = s
  101. }
  102. if err := http.ListenAndServe(":"+port, http.HandlerFunc(handleHTTP)); err != nil {
  103. log.Fatalf("http.ListenAndServe: %v", err)
  104. }
  105. }
  106. func installHealthChecker(mux *http.ServeMux) {
  107. // If no health check handler has been installed by this point, add a trivial one.
  108. const healthPath = "/_ah/health"
  109. hreq := &http.Request{
  110. Method: "GET",
  111. URL: &url.URL{
  112. Path: healthPath,
  113. },
  114. }
  115. if _, pat := mux.Handler(hreq); pat != healthPath {
  116. mux.HandleFunc(healthPath, func(w http.ResponseWriter, r *http.Request) {
  117. io.WriteString(w, "ok")
  118. })
  119. }
  120. }
  121. // NamespaceMods is a map from API service to a function that will mutate an RPC request to attach a namespace.
  122. // The function should be prepared to be called on the same message more than once; it should only modify the
  123. // RPC request the first time.
  124. var NamespaceMods = make(map[string]func(m proto.Message, namespace string))