service.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. /*
  2. *
  3. * Copyright 2021 Google LLC
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * https://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. // Package service is a utility for calling the S2A handshaker service.
  19. package service
  20. import (
  21. "context"
  22. "net"
  23. "os"
  24. "strings"
  25. "sync"
  26. "time"
  27. "google.golang.org/appengine"
  28. "google.golang.org/appengine/socket"
  29. grpc "google.golang.org/grpc"
  30. "google.golang.org/grpc/grpclog"
  31. )
  32. // An environment variable, if true, opportunistically use AppEngine-specific dialer to call S2A.
  33. const enableAppEngineDialerEnv = "S2A_ENABLE_APP_ENGINE_DIALER"
  34. var (
  35. // appEngineDialerHook is an AppEngine-specific dial option that is set
  36. // during init time. If nil, then the application is not running on Google
  37. // AppEngine.
  38. appEngineDialerHook func(context.Context) grpc.DialOption
  39. // mu guards hsConnMap and hsDialer.
  40. mu sync.Mutex
  41. // hsConnMap represents a mapping from an S2A handshaker service address
  42. // to a corresponding connection to an S2A handshaker service instance.
  43. hsConnMap = make(map[string]*grpc.ClientConn)
  44. // hsDialer will be reassigned in tests.
  45. hsDialer = grpc.Dial
  46. )
  47. func init() {
  48. if !appengine.IsAppEngine() && !appengine.IsDevAppServer() {
  49. return
  50. }
  51. appEngineDialerHook = func(ctx context.Context) grpc.DialOption {
  52. return grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) {
  53. return socket.DialTimeout(ctx, "tcp", addr, timeout)
  54. })
  55. }
  56. }
  57. // Dial dials the S2A handshaker service. If a connection has already been
  58. // established, this function returns it. Otherwise, a new connection is
  59. // created.
  60. func Dial(handshakerServiceAddress string) (*grpc.ClientConn, error) {
  61. mu.Lock()
  62. defer mu.Unlock()
  63. hsConn, ok := hsConnMap[handshakerServiceAddress]
  64. if !ok {
  65. // Create a new connection to the S2A handshaker service. Note that
  66. // this connection stays open until the application is closed.
  67. grpcOpts := []grpc.DialOption{
  68. grpc.WithInsecure(),
  69. }
  70. if enableAppEngineDialer() && appEngineDialerHook != nil {
  71. if grpclog.V(1) {
  72. grpclog.Info("Using AppEngine-specific dialer to talk to S2A.")
  73. }
  74. grpcOpts = append(grpcOpts, appEngineDialerHook(context.Background()))
  75. }
  76. var err error
  77. hsConn, err = hsDialer(handshakerServiceAddress, grpcOpts...)
  78. if err != nil {
  79. return nil, err
  80. }
  81. hsConnMap[handshakerServiceAddress] = hsConn
  82. }
  83. return hsConn, nil
  84. }
  85. func enableAppEngineDialer() bool {
  86. if strings.ToLower(os.Getenv(enableAppEngineDialerEnv)) == "true" {
  87. return true
  88. }
  89. return false
  90. }