summaryrefslogtreecommitdiffstats
path: root/vendor/google.golang.org/appengine/internal/api_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/google.golang.org/appengine/internal/api_test.go')
-rw-r--r--vendor/google.golang.org/appengine/internal/api_test.go466
1 files changed, 0 insertions, 466 deletions
diff --git a/vendor/google.golang.org/appengine/internal/api_test.go b/vendor/google.golang.org/appengine/internal/api_test.go
deleted file mode 100644
index 76624a28e..000000000
--- a/vendor/google.golang.org/appengine/internal/api_test.go
+++ /dev/null
@@ -1,466 +0,0 @@
-// Copyright 2014 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.
-
-// +build !appengine
-
-package internal
-
-import (
- "bufio"
- "bytes"
- "fmt"
- "io"
- "io/ioutil"
- "net/http"
- "net/http/httptest"
- "net/url"
- "os"
- "os/exec"
- "strings"
- "sync/atomic"
- "testing"
- "time"
-
- "github.com/golang/protobuf/proto"
- netcontext "golang.org/x/net/context"
-
- basepb "google.golang.org/appengine/internal/base"
- remotepb "google.golang.org/appengine/internal/remote_api"
-)
-
-const testTicketHeader = "X-Magic-Ticket-Header"
-
-func init() {
- ticketHeader = testTicketHeader
-}
-
-type fakeAPIHandler struct {
- hang chan int // used for RunSlowly RPC
-
- LogFlushes int32 // atomic
-}
-
-func (f *fakeAPIHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- writeResponse := func(res *remotepb.Response) {
- hresBody, err := proto.Marshal(res)
- if err != nil {
- http.Error(w, fmt.Sprintf("Failed encoding API response: %v", err), 500)
- return
- }
- w.Write(hresBody)
- }
-
- if r.URL.Path != "/rpc_http" {
- http.NotFound(w, r)
- return
- }
- hreqBody, err := ioutil.ReadAll(r.Body)
- if err != nil {
- http.Error(w, fmt.Sprintf("Bad body: %v", err), 500)
- return
- }
- apiReq := &remotepb.Request{}
- if err := proto.Unmarshal(hreqBody, apiReq); err != nil {
- http.Error(w, fmt.Sprintf("Bad encoded API request: %v", err), 500)
- return
- }
- if *apiReq.RequestId != "s3cr3t" && *apiReq.RequestId != DefaultTicket() {
- writeResponse(&remotepb.Response{
- RpcError: &remotepb.RpcError{
- Code: proto.Int32(int32(remotepb.RpcError_SECURITY_VIOLATION)),
- Detail: proto.String("bad security ticket"),
- },
- })
- return
- }
- if got, want := r.Header.Get(dapperHeader), "trace-001"; got != want {
- writeResponse(&remotepb.Response{
- RpcError: &remotepb.RpcError{
- Code: proto.Int32(int32(remotepb.RpcError_BAD_REQUEST)),
- Detail: proto.String(fmt.Sprintf("trace info = %q, want %q", got, want)),
- },
- })
- return
- }
-
- service, method := *apiReq.ServiceName, *apiReq.Method
- var resOut proto.Message
- if service == "actordb" && method == "LookupActor" {
- req := &basepb.StringProto{}
- res := &basepb.StringProto{}
- if err := proto.Unmarshal(apiReq.Request, req); err != nil {
- http.Error(w, fmt.Sprintf("Bad encoded request: %v", err), 500)
- return
- }
- if *req.Value == "Doctor Who" {
- res.Value = proto.String("David Tennant")
- }
- resOut = res
- }
- if service == "errors" {
- switch method {
- case "Non200":
- http.Error(w, "I'm a little teapot.", 418)
- return
- case "ShortResponse":
- w.Header().Set("Content-Length", "100")
- w.Write([]byte("way too short"))
- return
- case "OverQuota":
- writeResponse(&remotepb.Response{
- RpcError: &remotepb.RpcError{
- Code: proto.Int32(int32(remotepb.RpcError_OVER_QUOTA)),
- Detail: proto.String("you are hogging the resources!"),
- },
- })
- return
- case "RunSlowly":
- // TestAPICallRPCFailure creates f.hang, but does not strobe it
- // until Call returns with remotepb.RpcError_CANCELLED.
- // This is here to force a happens-before relationship between
- // the httptest server handler and shutdown.
- <-f.hang
- resOut = &basepb.VoidProto{}
- }
- }
- if service == "logservice" && method == "Flush" {
- // Pretend log flushing is slow.
- time.Sleep(50 * time.Millisecond)
- atomic.AddInt32(&f.LogFlushes, 1)
- resOut = &basepb.VoidProto{}
- }
-
- encOut, err := proto.Marshal(resOut)
- if err != nil {
- http.Error(w, fmt.Sprintf("Failed encoding response: %v", err), 500)
- return
- }
- writeResponse(&remotepb.Response{
- Response: encOut,
- })
-}
-
-func setup() (f *fakeAPIHandler, c *context, cleanup func()) {
- f = &fakeAPIHandler{}
- srv := httptest.NewServer(f)
- u, err := url.Parse(srv.URL + apiPath)
- if err != nil {
- panic(fmt.Sprintf("url.Parse(%q): %v", srv.URL+apiPath, err))
- }
- return f, &context{
- req: &http.Request{
- Header: http.Header{
- ticketHeader: []string{"s3cr3t"},
- dapperHeader: []string{"trace-001"},
- },
- },
- apiURL: u,
- }, srv.Close
-}
-
-func TestAPICall(t *testing.T) {
- _, c, cleanup := setup()
- defer cleanup()
-
- req := &basepb.StringProto{
- Value: proto.String("Doctor Who"),
- }
- res := &basepb.StringProto{}
- err := Call(toContext(c), "actordb", "LookupActor", req, res)
- if err != nil {
- t.Fatalf("API call failed: %v", err)
- }
- if got, want := *res.Value, "David Tennant"; got != want {
- t.Errorf("Response is %q, want %q", got, want)
- }
-}
-
-func TestAPICallTicketUnavailable(t *testing.T) {
- resetEnv := SetTestEnv()
- defer resetEnv()
- _, c, cleanup := setup()
- defer cleanup()
-
- c.req.Header.Set(ticketHeader, "")
- req := &basepb.StringProto{
- Value: proto.String("Doctor Who"),
- }
- res := &basepb.StringProto{}
- err := Call(toContext(c), "actordb", "LookupActor", req, res)
- if err != nil {
- t.Fatalf("API call failed: %v", err)
- }
- if got, want := *res.Value, "David Tennant"; got != want {
- t.Errorf("Response is %q, want %q", got, want)
- }
-}
-
-func TestAPICallRPCFailure(t *testing.T) {
- f, c, cleanup := setup()
- defer cleanup()
-
- testCases := []struct {
- method string
- code remotepb.RpcError_ErrorCode
- }{
- {"Non200", remotepb.RpcError_UNKNOWN},
- {"ShortResponse", remotepb.RpcError_UNKNOWN},
- {"OverQuota", remotepb.RpcError_OVER_QUOTA},
- {"RunSlowly", remotepb.RpcError_CANCELLED},
- }
- f.hang = make(chan int) // only for RunSlowly
- for _, tc := range testCases {
- ctx, _ := netcontext.WithTimeout(toContext(c), 100*time.Millisecond)
- err := Call(ctx, "errors", tc.method, &basepb.VoidProto{}, &basepb.VoidProto{})
- ce, ok := err.(*CallError)
- if !ok {
- t.Errorf("%s: API call error is %T (%v), want *CallError", tc.method, err, err)
- continue
- }
- if ce.Code != int32(tc.code) {
- t.Errorf("%s: ce.Code = %d, want %d", tc.method, ce.Code, tc.code)
- }
- if tc.method == "RunSlowly" {
- f.hang <- 1 // release the HTTP handler
- }
- }
-}
-
-func TestAPICallDialFailure(t *testing.T) {
- // See what happens if the API host is unresponsive.
- // This should time out quickly, not hang forever.
- _, c, cleanup := setup()
- defer cleanup()
- // Reset the URL to the production address so that dialing fails.
- c.apiURL = apiURL()
-
- start := time.Now()
- err := Call(toContext(c), "foo", "bar", &basepb.VoidProto{}, &basepb.VoidProto{})
- const max = 1 * time.Second
- if taken := time.Since(start); taken > max {
- t.Errorf("Dial hang took too long: %v > %v", taken, max)
- }
- if err == nil {
- t.Error("Call did not fail")
- }
-}
-
-func TestDelayedLogFlushing(t *testing.T) {
- f, c, cleanup := setup()
- defer cleanup()
-
- http.HandleFunc("/quick_log", func(w http.ResponseWriter, r *http.Request) {
- logC := WithContext(netcontext.Background(), r)
- fromContext(logC).apiURL = c.apiURL // Otherwise it will try to use the default URL.
- Logf(logC, 1, "It's a lovely day.")
- w.WriteHeader(200)
- w.Write(make([]byte, 100<<10)) // write 100 KB to force HTTP flush
- })
-
- r := &http.Request{
- Method: "GET",
- URL: &url.URL{
- Scheme: "http",
- Path: "/quick_log",
- },
- Header: c.req.Header,
- Body: ioutil.NopCloser(bytes.NewReader(nil)),
- }
- w := httptest.NewRecorder()
-
- // Check that log flushing does not hold up the HTTP response.
- start := time.Now()
- handleHTTP(w, r)
- if d := time.Since(start); d > 10*time.Millisecond {
- t.Errorf("handleHTTP took %v, want under 10ms", d)
- }
- const hdr = "X-AppEngine-Log-Flush-Count"
- if h := w.HeaderMap.Get(hdr); h != "1" {
- t.Errorf("%s header = %q, want %q", hdr, h, "1")
- }
- if f := atomic.LoadInt32(&f.LogFlushes); f != 0 {
- t.Errorf("After HTTP response: f.LogFlushes = %d, want 0", f)
- }
-
- // Check that the log flush eventually comes in.
- time.Sleep(100 * time.Millisecond)
- if f := atomic.LoadInt32(&f.LogFlushes); f != 1 {
- t.Errorf("After 100ms: f.LogFlushes = %d, want 1", f)
- }
-}
-
-func TestRemoteAddr(t *testing.T) {
- var addr string
- http.HandleFunc("/remote_addr", func(w http.ResponseWriter, r *http.Request) {
- addr = r.RemoteAddr
- })
-
- testCases := []struct {
- headers http.Header
- addr string
- }{
- {http.Header{"X-Appengine-User-Ip": []string{"10.5.2.1"}}, "10.5.2.1:80"},
- {http.Header{"X-Appengine-Remote-Addr": []string{"1.2.3.4"}}, "1.2.3.4:80"},
- {http.Header{"X-Appengine-Remote-Addr": []string{"1.2.3.4:8080"}}, "1.2.3.4:8080"},
- {
- http.Header{"X-Appengine-Remote-Addr": []string{"2401:fa00:9:1:7646:a0ff:fe90:ca66"}},
- "[2401:fa00:9:1:7646:a0ff:fe90:ca66]:80",
- },
- {
- http.Header{"X-Appengine-Remote-Addr": []string{"[::1]:http"}},
- "[::1]:http",
- },
- {http.Header{}, "127.0.0.1:80"},
- }
-
- for _, tc := range testCases {
- r := &http.Request{
- Method: "GET",
- URL: &url.URL{Scheme: "http", Path: "/remote_addr"},
- Header: tc.headers,
- Body: ioutil.NopCloser(bytes.NewReader(nil)),
- }
- handleHTTP(httptest.NewRecorder(), r)
- if addr != tc.addr {
- t.Errorf("Header %v, got %q, want %q", tc.headers, addr, tc.addr)
- }
- }
-}
-
-func TestPanickingHandler(t *testing.T) {
- http.HandleFunc("/panic", func(http.ResponseWriter, *http.Request) {
- panic("whoops!")
- })
- r := &http.Request{
- Method: "GET",
- URL: &url.URL{Scheme: "http", Path: "/panic"},
- Body: ioutil.NopCloser(bytes.NewReader(nil)),
- }
- rec := httptest.NewRecorder()
- handleHTTP(rec, r)
- if rec.Code != 500 {
- t.Errorf("Panicking handler returned HTTP %d, want HTTP %d", rec.Code, 500)
- }
-}
-
-var raceDetector = false
-
-func TestAPICallAllocations(t *testing.T) {
- if raceDetector {
- t.Skip("not running under race detector")
- }
-
- // Run the test API server in a subprocess so we aren't counting its allocations.
- u, cleanup := launchHelperProcess(t)
- defer cleanup()
- c := &context{
- req: &http.Request{
- Header: http.Header{
- ticketHeader: []string{"s3cr3t"},
- dapperHeader: []string{"trace-001"},
- },
- },
- apiURL: u,
- }
-
- req := &basepb.StringProto{
- Value: proto.String("Doctor Who"),
- }
- res := &basepb.StringProto{}
- var apiErr error
- avg := testing.AllocsPerRun(100, func() {
- ctx, _ := netcontext.WithTimeout(toContext(c), 100*time.Millisecond)
- if err := Call(ctx, "actordb", "LookupActor", req, res); err != nil && apiErr == nil {
- apiErr = err // get the first error only
- }
- })
- if apiErr != nil {
- t.Errorf("API call failed: %v", apiErr)
- }
-
- // Lots of room for improvement...
- // TODO(djd): Reduce maximum to 85 once the App Engine SDK is based on 1.6.
- const min, max float64 = 70, 100
- if avg < min || max < avg {
- t.Errorf("Allocations per API call = %g, want in [%g,%g]", avg, min, max)
- }
-}
-
-func launchHelperProcess(t *testing.T) (apiURL *url.URL, cleanup func()) {
- cmd := exec.Command(os.Args[0], "-test.run=TestHelperProcess")
- cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
- stdin, err := cmd.StdinPipe()
- if err != nil {
- t.Fatalf("StdinPipe: %v", err)
- }
- stdout, err := cmd.StdoutPipe()
- if err != nil {
- t.Fatalf("StdoutPipe: %v", err)
- }
- if err := cmd.Start(); err != nil {
- t.Fatalf("Starting helper process: %v", err)
- }
-
- scan := bufio.NewScanner(stdout)
- var u *url.URL
- for scan.Scan() {
- line := scan.Text()
- if hp := strings.TrimPrefix(line, helperProcessMagic); hp != line {
- var err error
- u, err = url.Parse(hp)
- if err != nil {
- t.Fatalf("Failed to parse %q: %v", hp, err)
- }
- break
- }
- }
- if err := scan.Err(); err != nil {
- t.Fatalf("Scanning helper process stdout: %v", err)
- }
- if u == nil {
- t.Fatal("Helper process never reported")
- }
-
- return u, func() {
- stdin.Close()
- if err := cmd.Wait(); err != nil {
- t.Errorf("Helper process did not exit cleanly: %v", err)
- }
- }
-}
-
-const helperProcessMagic = "A lovely helper process is listening at "
-
-// This isn't a real test. It's used as a helper process.
-func TestHelperProcess(*testing.T) {
- if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
- return
- }
- defer os.Exit(0)
-
- f := &fakeAPIHandler{}
- srv := httptest.NewServer(f)
- defer srv.Close()
- fmt.Println(helperProcessMagic + srv.URL + apiPath)
-
- // Wait for stdin to be closed.
- io.Copy(ioutil.Discard, os.Stdin)
-}
-
-func TestBackgroundContext(t *testing.T) {
- resetEnv := SetTestEnv()
- defer resetEnv()
-
- ctx, key := fromContext(BackgroundContext()), "X-Magic-Ticket-Header"
- if g, w := ctx.req.Header.Get(key), "my-app-id/default.20150612t184001.0"; g != w {
- t.Errorf("%v = %q, want %q", key, g, w)
- }
-
- // Check that using the background context doesn't panic.
- req := &basepb.StringProto{
- Value: proto.String("Doctor Who"),
- }
- res := &basepb.StringProto{}
- Call(BackgroundContext(), "actordb", "LookupActor", req, res) // expected to fail
-}