| // Copyright 2018 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package proxy |
| |
| import ( |
| "io/ioutil" |
| "net/http" |
| "net/url" |
| "strings" |
| "testing" |
| |
| "cloud.google.com/go/internal/testutil" |
| "github.com/google/go-cmp/cmp/cmpopts" |
| "github.com/google/martian/v3" |
| ) |
| |
| func TestLogger(t *testing.T) { |
| req := &http.Request{ |
| Method: "POST", |
| URL: &url.URL{ |
| Scheme: "https", |
| Host: "example.com", |
| Path: "a/b/c", |
| }, |
| Header: http.Header{"H1": {"v1", "v2"}, "Content-Type": {"text/plain"}}, |
| Body: ioutil.NopCloser(strings.NewReader("hello")), |
| Trailer: http.Header{"T1": {"v3", "v4"}}, |
| } |
| res := &http.Response{ |
| Request: req, |
| StatusCode: 204, |
| Body: ioutil.NopCloser(strings.NewReader("goodbye")), |
| Header: http.Header{"H2": {"v5"}}, |
| Trailer: http.Header{"T2": {"v6", "v7"}}, |
| } |
| l := newLogger() |
| _, remove, err := martian.TestContext(req, nil, nil) |
| if err != nil { |
| t.Fatal(err) |
| } |
| defer remove() |
| if err := l.ModifyRequest(req); err != nil { |
| t.Fatal(err) |
| } |
| if err := l.ModifyResponse(res); err != nil { |
| t.Fatal(err) |
| } |
| lg := l.Extract() |
| want := []*Entry{ |
| { |
| ID: lg.Entries[0].ID, |
| Request: &Request{ |
| Method: "POST", |
| URL: "https://example.com/a/b/c", |
| Header: http.Header{"H1": {"v1", "v2"}}, |
| MediaType: "text/plain", |
| BodyParts: [][]byte{[]byte("hello")}, |
| Trailer: http.Header{"T1": {"v3", "v4"}}, |
| }, |
| Response: &Response{ |
| StatusCode: 204, |
| Body: []byte("goodbye"), |
| Header: http.Header{"H2": {"v5"}}, |
| Trailer: http.Header{"T2": {"v6", "v7"}}, |
| }, |
| }, |
| } |
| if diff := testutil.Diff(lg.Entries, want); diff != "" { |
| t.Error(diff) |
| } |
| } |
| |
| func TestToHTTPResponse(t *testing.T) { |
| for _, test := range []struct { |
| desc string |
| lr *Response |
| req *http.Request |
| want *http.Response |
| }{ |
| { |
| desc: "GET request", |
| lr: &Response{ |
| StatusCode: 201, |
| Proto: "1.1", |
| Header: http.Header{"h": {"v"}}, |
| Body: []byte("text"), |
| }, |
| req: &http.Request{Method: "GET"}, |
| want: &http.Response{ |
| Request: &http.Request{Method: "GET"}, |
| StatusCode: 201, |
| Proto: "1.1", |
| Header: http.Header{"h": {"v"}}, |
| ContentLength: 4, |
| }, |
| }, |
| { |
| desc: "HEAD request with no Content-Length header", |
| lr: &Response{ |
| StatusCode: 201, |
| Proto: "1.1", |
| Header: http.Header{"h": {"v"}}, |
| Body: []byte("text"), |
| }, |
| req: &http.Request{Method: "HEAD"}, |
| want: &http.Response{ |
| Request: &http.Request{Method: "HEAD"}, |
| StatusCode: 201, |
| Proto: "1.1", |
| Header: http.Header{"h": {"v"}}, |
| ContentLength: -1, |
| }, |
| }, |
| { |
| desc: "HEAD request with Content-Length header", |
| lr: &Response{ |
| StatusCode: 201, |
| Proto: "1.1", |
| Header: http.Header{"h": {"v"}, "Content-Length": {"17"}}, |
| Body: []byte("text"), |
| }, |
| req: &http.Request{Method: "HEAD"}, |
| want: &http.Response{ |
| Request: &http.Request{Method: "HEAD"}, |
| StatusCode: 201, |
| Proto: "1.1", |
| Header: http.Header{"h": {"v"}, "Content-Length": {"17"}}, |
| ContentLength: 17, |
| }, |
| }, |
| } { |
| got := toHTTPResponse(test.lr, test.req) |
| got.Body = nil |
| if diff := testutil.Diff(got, test.want, cmpopts.IgnoreUnexported(http.Request{})); diff != "" { |
| t.Errorf("%s: %s", test.desc, diff) |
| } |
| } |
| } |
| |
| func TestEmptyBody(t *testing.T) { |
| // Verify that a zero-length body is nil after logging. |
| // That will ensure that net/http sends a "Content-Length: 0" header. |
| req := &http.Request{ |
| Method: "POST", |
| URL: &url.URL{ |
| Scheme: "https", |
| Host: "example.com", |
| Path: "a/b/c", |
| }, |
| Body: ioutil.NopCloser(strings.NewReader("")), |
| } |
| l := newLogger() |
| _, remove, err := martian.TestContext(req, nil, nil) |
| if err != nil { |
| t.Fatal(err) |
| } |
| defer remove() |
| if err := l.ModifyRequest(req); err != nil { |
| t.Fatal(err) |
| } |
| if req.Body != nil { |
| t.Error("got non-nil req.Body, want nil") |
| } |
| } |