PageRenderTime 42ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/Godeps/_workspace/src/google.golang.org/appengine/remote_api/client.go

https://github.com/ironcladlou/kubernetes
Go | 173 lines | 131 code | 28 blank | 14 comment | 34 complexity | a88e55977effc7d7f2721cf72ac4ef96 MD5 | raw file
Possible License(s): BSD-3-Clause, MIT, LGPL-3.0, Apache-2.0, JSON, BSD-2-Clause
  1. // Copyright 2013 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 remote_api
  5. // This file provides the client for connecting remotely to a user's production
  6. // application.
  7. import (
  8. "bytes"
  9. "fmt"
  10. "io/ioutil"
  11. "log"
  12. "math/rand"
  13. "net/http"
  14. "net/url"
  15. "regexp"
  16. "strconv"
  17. "strings"
  18. "time"
  19. "github.com/golang/protobuf/proto"
  20. "google.golang.org/appengine"
  21. "google.golang.org/appengine/internal"
  22. pb "google.golang.org/appengine/internal/remote_api"
  23. )
  24. // NewRemoteContext returns a context that gives access to the production
  25. // APIs for the application at the given host. All communication will be
  26. // performed over SSL unless the host is localhost.
  27. func NewRemoteContext(host string, client *http.Client) (appengine.Context, error) {
  28. // Add an appcfg header to outgoing requests.
  29. t := client.Transport
  30. if t == nil {
  31. t = http.DefaultTransport
  32. }
  33. client.Transport = &headerAddingRoundTripper{t}
  34. url := url.URL{
  35. Scheme: "https",
  36. Host: host,
  37. Path: "/_ah/remote_api",
  38. }
  39. if host == "localhost" || strings.HasPrefix(host, "localhost:") {
  40. url.Scheme = "http"
  41. }
  42. u := url.String()
  43. appID, err := getAppID(client, u)
  44. if err != nil {
  45. return nil, fmt.Errorf("unable to contact server: %v", err)
  46. }
  47. return &context{
  48. client: client,
  49. url: u,
  50. appID: appID,
  51. }, nil
  52. }
  53. type context struct {
  54. client *http.Client
  55. url string
  56. appID string
  57. }
  58. func (c *context) Request() interface{} { return nil }
  59. func (c *context) FullyQualifiedAppID() string { return c.appID }
  60. func (c *context) logf(level, format string, args ...interface{}) {
  61. log.Printf(level+": "+format, args...)
  62. }
  63. func (c *context) Debugf(format string, args ...interface{}) { c.logf("DEBUG", format, args...) }
  64. func (c *context) Infof(format string, args ...interface{}) { c.logf("INFO", format, args...) }
  65. func (c *context) Warningf(format string, args ...interface{}) { c.logf("WARNING", format, args...) }
  66. func (c *context) Errorf(format string, args ...interface{}) { c.logf("ERROR", format, args...) }
  67. func (c *context) Criticalf(format string, args ...interface{}) { c.logf("CRITICAL", format, args...) }
  68. func (c *context) Call(service, method string, in, out proto.Message, opts *internal.CallOptions) error {
  69. req, err := proto.Marshal(in)
  70. if err != nil {
  71. return fmt.Errorf("error marshalling request: %v", err)
  72. }
  73. remReq := &pb.Request{
  74. ServiceName: proto.String(service),
  75. Method: proto.String(method),
  76. Request: req,
  77. // NOTE(djd): RequestId is unused in the server.
  78. }
  79. req, err = proto.Marshal(remReq)
  80. if err != nil {
  81. return fmt.Errorf("proto.Marshal: %v", err)
  82. }
  83. // TODO(djd): Respect opts.Timeout?
  84. resp, err := c.client.Post(c.url, "application/octet-stream", bytes.NewReader(req))
  85. if err != nil {
  86. return fmt.Errorf("error sending request: %v", err)
  87. }
  88. defer resp.Body.Close()
  89. body, err := ioutil.ReadAll(resp.Body)
  90. if resp.StatusCode != http.StatusOK {
  91. return fmt.Errorf("bad response %d; body: %q", resp.StatusCode, body)
  92. }
  93. if err != nil {
  94. return fmt.Errorf("failed reading response: %v", err)
  95. }
  96. remResp := &pb.Response{}
  97. if err := proto.Unmarshal(body, remResp); err != nil {
  98. return fmt.Errorf("error unmarshalling response: %v", err)
  99. }
  100. if ae := remResp.GetApplicationError(); ae != nil {
  101. return &internal.APIError{
  102. Code: ae.GetCode(),
  103. Detail: ae.GetDetail(),
  104. Service: service,
  105. }
  106. }
  107. if remResp.Response == nil {
  108. return fmt.Errorf("unexpected response: %s", proto.MarshalTextString(remResp))
  109. }
  110. return proto.Unmarshal(remResp.Response, out)
  111. }
  112. // This is a forgiving regexp designed to parse the app ID from YAML.
  113. var appIDRE = regexp.MustCompile(`app_id["']?\s*:\s*['"]?([-a-z0-9.:~]+)`)
  114. func getAppID(client *http.Client, url string) (string, error) {
  115. // Generate a pseudo-random token for handshaking.
  116. token := strconv.Itoa(rand.New(rand.NewSource(time.Now().UnixNano())).Int())
  117. resp, err := client.Get(fmt.Sprintf("%s?rtok=%s", url, token))
  118. if err != nil {
  119. return "", err
  120. }
  121. defer resp.Body.Close()
  122. body, err := ioutil.ReadAll(resp.Body)
  123. if resp.StatusCode != http.StatusOK {
  124. return "", fmt.Errorf("bad response %d; body: %q", resp.StatusCode, body)
  125. }
  126. if err != nil {
  127. return "", fmt.Errorf("failed reading response: %v", err)
  128. }
  129. // Check the token is present in response.
  130. if !bytes.Contains(body, []byte(token)) {
  131. return "", fmt.Errorf("token not found: want %q; body %q", token, body)
  132. }
  133. match := appIDRE.FindSubmatch(body)
  134. if match == nil {
  135. return "", fmt.Errorf("app ID not found: body %q", body)
  136. }
  137. return string(match[1]), nil
  138. }
  139. type headerAddingRoundTripper struct {
  140. Wrapped http.RoundTripper
  141. }
  142. func (t *headerAddingRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) {
  143. r.Header.Set("X-Appcfg-Api-Version", "1")
  144. return t.Wrapped.RoundTrip(r)
  145. }