You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

231 lines
5.5 KiB

  1. // Copyright 2018 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // +build go1.8
  15. package main_test
  16. import (
  17. "crypto/tls"
  18. "crypto/x509"
  19. "errors"
  20. "fmt"
  21. "io/ioutil"
  22. "net"
  23. "net/http"
  24. "net/url"
  25. "os"
  26. "os/exec"
  27. "strings"
  28. "testing"
  29. "time"
  30. "cloud.google.com/go/internal/testutil"
  31. "cloud.google.com/go/storage"
  32. "golang.org/x/net/context"
  33. "golang.org/x/oauth2"
  34. "google.golang.org/api/option"
  35. )
  36. const initial = "initial state"
  37. func TestIntegration_HTTPR(t *testing.T) {
  38. if testing.Short() {
  39. t.Skip("Integration tests skipped in short mode")
  40. }
  41. if testutil.ProjID() == "" {
  42. t.Fatal("set GCLOUD_TESTS_GOLANG_PROJECT_ID and GCLOUD_TESTS_GOLANG_KEY")
  43. }
  44. // Get a unique temporary filename.
  45. f, err := ioutil.TempFile("", "httpreplay")
  46. if err != nil {
  47. t.Fatal(err)
  48. }
  49. replayFilename := f.Name()
  50. if err := f.Close(); err != nil {
  51. t.Fatal(err)
  52. }
  53. defer os.Remove(replayFilename)
  54. if err := exec.Command("go", "build").Run(); err != nil {
  55. t.Fatalf("running 'go build': %v", err)
  56. }
  57. defer os.Remove("./httpr")
  58. want := runRecord(t, replayFilename)
  59. got := runReplay(t, replayFilename)
  60. if got != want {
  61. t.Fatalf("got %q, want %q", got, want)
  62. }
  63. }
  64. func runRecord(t *testing.T, filename string) string {
  65. cmd, tr, cport, err := start("-record", filename)
  66. if err != nil {
  67. t.Fatal(err)
  68. }
  69. defer stop(t, cmd)
  70. ctx := context.Background()
  71. hc := &http.Client{
  72. Transport: &oauth2.Transport{
  73. Base: tr,
  74. Source: testutil.TokenSource(ctx, storage.ScopeFullControl),
  75. },
  76. }
  77. res, err := http.Post(
  78. fmt.Sprintf("http://localhost:%s/initial", cport),
  79. "text/plain",
  80. strings.NewReader(initial))
  81. if err != nil {
  82. t.Fatal(err)
  83. }
  84. if res.StatusCode != 200 {
  85. t.Fatalf("from POST: %s", res.Status)
  86. }
  87. info, err := getBucketInfo(ctx, hc)
  88. if err != nil {
  89. t.Fatal(err)
  90. }
  91. return info
  92. }
  93. func runReplay(t *testing.T, filename string) string {
  94. cmd, tr, cport, err := start("-replay", filename)
  95. if err != nil {
  96. t.Fatal(err)
  97. }
  98. defer stop(t, cmd)
  99. hc := &http.Client{Transport: tr}
  100. res, err := http.Get(fmt.Sprintf("http://localhost:%s/initial", cport))
  101. if err != nil {
  102. t.Fatal(err)
  103. }
  104. if res.StatusCode != 200 {
  105. t.Fatalf("from GET: %s", res.Status)
  106. }
  107. bytes, err := ioutil.ReadAll(res.Body)
  108. res.Body.Close()
  109. if err != nil {
  110. t.Fatal(err)
  111. }
  112. if got, want := string(bytes), initial; got != want {
  113. t.Errorf("initial: got %q, want %q", got, want)
  114. }
  115. info, err := getBucketInfo(context.Background(), hc)
  116. if err != nil {
  117. t.Fatal(err)
  118. }
  119. return info
  120. }
  121. // Start the proxy binary and wait for it to come up.
  122. // Return a transport that talks to the proxy, as well as the control port.
  123. // modeFlag must be either "-record" or "-replay".
  124. func start(modeFlag, filename string) (*exec.Cmd, *http.Transport, string, error) {
  125. pport, err := pickPort()
  126. if err != nil {
  127. return nil, nil, "", err
  128. }
  129. cport, err := pickPort()
  130. if err != nil {
  131. return nil, nil, "", err
  132. }
  133. cmd := exec.Command("./httpr", "-port", pport, "-control-port", cport, modeFlag, filename, "-debug-headers")
  134. if err := cmd.Start(); err != nil {
  135. return nil, nil, "", err
  136. }
  137. // Wait for the server to come up.
  138. serverUp := false
  139. for i := 0; i < 10; i++ {
  140. if conn, err := net.Dial("tcp", "localhost:"+cport); err == nil {
  141. conn.Close()
  142. serverUp = true
  143. break
  144. }
  145. time.Sleep(time.Second)
  146. }
  147. if !serverUp {
  148. return nil, nil, "", errors.New("server never came up")
  149. }
  150. tr, err := proxyTransport(pport, cport)
  151. if err != nil {
  152. return nil, nil, "", err
  153. }
  154. return cmd, tr, cport, nil
  155. }
  156. func stop(t *testing.T, cmd *exec.Cmd) {
  157. if err := cmd.Process.Signal(os.Interrupt); err != nil {
  158. t.Fatal(err)
  159. }
  160. }
  161. // pickPort picks an unused port.
  162. func pickPort() (string, error) {
  163. l, err := net.Listen("tcp", ":0")
  164. if err != nil {
  165. return "", err
  166. }
  167. addr := l.Addr().String()
  168. _, port, err := net.SplitHostPort(addr)
  169. if err != nil {
  170. return "", err
  171. }
  172. l.Close()
  173. return port, nil
  174. }
  175. func proxyTransport(pport, cport string) (*http.Transport, error) {
  176. caCert, err := getBody(fmt.Sprintf("http://localhost:%s/authority.cer", cport))
  177. if err != nil {
  178. return nil, err
  179. }
  180. caCertPool := x509.NewCertPool()
  181. if !caCertPool.AppendCertsFromPEM([]byte(caCert)) {
  182. return nil, errors.New("bad CA Cert")
  183. }
  184. return &http.Transport{
  185. Proxy: http.ProxyURL(&url.URL{Host: "localhost:" + pport}),
  186. TLSClientConfig: &tls.Config{RootCAs: caCertPool},
  187. }, nil
  188. }
  189. func getBucketInfo(ctx context.Context, hc *http.Client) (string, error) {
  190. client, err := storage.NewClient(ctx, option.WithHTTPClient(hc))
  191. if err != nil {
  192. return "", err
  193. }
  194. defer client.Close()
  195. b := client.Bucket(testutil.ProjID())
  196. attrs, err := b.Attrs(ctx)
  197. if err != nil {
  198. return "", err
  199. }
  200. return fmt.Sprintf("name:%s reqpays:%v location:%s sclass:%s",
  201. attrs.Name, attrs.RequesterPays, attrs.Location, attrs.StorageClass), nil
  202. }
  203. func getBody(url string) ([]byte, error) {
  204. res, err := http.Get(url)
  205. if err != nil {
  206. return nil, err
  207. }
  208. if res.StatusCode != 200 {
  209. return nil, fmt.Errorf("response: %s", res.Status)
  210. }
  211. defer res.Body.Close()
  212. return ioutil.ReadAll(res.Body)
  213. }