github.com/google/martian/v3@v3.3.3/har/har_test.go (about)

     1  // Copyright 2015 Google Inc. All rights reserved.
     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  
    15  package har
    16  
    17  import (
    18  	"bytes"
    19  	"encoding/json"
    20  	"mime/multipart"
    21  	"net/http"
    22  	"reflect"
    23  	"strings"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/google/martian/v3"
    28  	"github.com/google/martian/v3/proxyutil"
    29  )
    30  
    31  func TestModifyRequest(t *testing.T) {
    32  	req, err := http.NewRequest("GET", "http://example.com/path?query=true", nil)
    33  	if err != nil {
    34  		t.Fatalf("http.NewRequest(): got %v, want no error", err)
    35  	}
    36  	req.Header.Add("Request-Header", "first")
    37  	req.Header.Add("Request-Header", "second")
    38  
    39  	cookie := &http.Cookie{
    40  		Name:  "request",
    41  		Value: "cookie",
    42  	}
    43  	req.AddCookie(cookie)
    44  
    45  	_, remove, err := martian.TestContext(req, nil, nil)
    46  	if err != nil {
    47  		t.Fatalf("martian.TestContext(): got %v, want no error", err)
    48  	}
    49  	defer remove()
    50  
    51  	logger := NewLogger()
    52  	if err := logger.ModifyRequest(req); err != nil {
    53  		t.Fatalf("ModifyRequest(): got %v, want no error", err)
    54  	}
    55  
    56  	log := logger.Export().Log
    57  	if got, want := log.Version, "1.2"; got != want {
    58  		t.Errorf("log.Version: got %q, want %q", got, want)
    59  	}
    60  
    61  	if got, want := len(log.Entries), 1; got != want {
    62  		t.Fatalf("len(log.Entries): got %d, want %d", got, want)
    63  	}
    64  
    65  	entry := log.Entries[0]
    66  	if got, want := time.Since(entry.StartedDateTime), time.Second; got > want {
    67  		t.Errorf("entry.StartedDateTime: got %s, want less than %s", got, want)
    68  	}
    69  
    70  	hreq := entry.Request
    71  	if got, want := hreq.Method, "GET"; got != want {
    72  		t.Errorf("hreq.Method: got %q, want %q", got, want)
    73  	}
    74  
    75  	if got, want := hreq.URL, "http://example.com/path?query=true"; got != want {
    76  		t.Errorf("hreq.URL: got %q, want %q", got, want)
    77  	}
    78  
    79  	if got, want := hreq.HTTPVersion, "HTTP/1.1"; got != want {
    80  		t.Errorf("hreq.HTTPVersion: got %q, want %q", got, want)
    81  	}
    82  
    83  	if got, want := hreq.BodySize, int64(0); got != want {
    84  		t.Errorf("hreq.BodySize: got %d, want %d", got, want)
    85  	}
    86  
    87  	if got, want := hreq.HeadersSize, int64(-1); got != want {
    88  		t.Errorf("hreq.HeadersSize: got %d, want %d", got, want)
    89  	}
    90  
    91  	if got, want := len(hreq.QueryString), 1; got != want {
    92  		t.Fatalf("len(hreq.QueryString): got %d, want %q", got, want)
    93  	}
    94  
    95  	qs := hreq.QueryString[0]
    96  	if got, want := qs.Name, "query"; got != want {
    97  		t.Errorf("qs.Name: got %q, want %q", got, want)
    98  	}
    99  	if got, want := qs.Value, "true"; got != want {
   100  		t.Errorf("qs.Value: got %q, want %q", got, want)
   101  	}
   102  
   103  	wantHeaders := http.Header{
   104  		"Request-Header": {"first", "second"},
   105  		"Cookie":         {cookie.String()},
   106  		"Host":           {"example.com"},
   107  	}
   108  	if got := headersToHTTP(hreq.Headers); !reflect.DeepEqual(got, wantHeaders) {
   109  		t.Errorf("headers:\ngot:\n%+v\nwant:\n%+v", got, wantHeaders)
   110  	}
   111  
   112  	if got, want := len(hreq.Cookies), 1; got != want {
   113  		t.Fatalf("len(hreq.Cookies): got %d, want %d", got, want)
   114  	}
   115  
   116  	hcookie := hreq.Cookies[0]
   117  	if got, want := hcookie.Name, "request"; got != want {
   118  		t.Errorf("hcookie.Name: got %q, want %q", got, want)
   119  	}
   120  	if got, want := hcookie.Value, "cookie"; got != want {
   121  		t.Errorf("hcookie.Value: got %q, want %q", got, want)
   122  	}
   123  }
   124  
   125  func headersToHTTP(hs []Header) http.Header {
   126  	hh := http.Header{}
   127  	for _, h := range hs {
   128  		hh[h.Name] = append(hh[h.Name], h.Value)
   129  	}
   130  	return hh
   131  }
   132  
   133  func TestModifyResponse(t *testing.T) {
   134  	req, err := http.NewRequest("GET", "http://example.com", nil)
   135  	if err != nil {
   136  		t.Fatalf("NewRequest(): got %v, want no error", err)
   137  	}
   138  
   139  	_, remove, err := martian.TestContext(req, nil, nil)
   140  	if err != nil {
   141  		t.Fatalf("martian.TestContext(): got %v, want no error", err)
   142  	}
   143  	defer remove()
   144  
   145  	res := proxyutil.NewResponse(301, strings.NewReader("response body"), req)
   146  	res.ContentLength = 13
   147  	res.Header.Add("Response-Header", "first")
   148  	res.Header.Add("Response-Header", "second")
   149  	res.Header.Set("Location", "google.com")
   150  
   151  	expires := time.Now()
   152  	cookie := &http.Cookie{
   153  		Name:     "response",
   154  		Value:    "cookie",
   155  		Path:     "/",
   156  		Domain:   "example.com",
   157  		Expires:  expires,
   158  		Secure:   true,
   159  		HttpOnly: true,
   160  	}
   161  	res.Header.Set("Set-Cookie", cookie.String())
   162  
   163  	logger := NewLogger()
   164  
   165  	if err := logger.ModifyRequest(req); err != nil {
   166  		t.Fatalf("ModifyRequest(): got %v, want no error", err)
   167  	}
   168  
   169  	if err := logger.ModifyResponse(res); err != nil {
   170  		t.Fatalf("ModifyResponse(): got %v, want no error", err)
   171  	}
   172  
   173  	log := logger.Export().Log
   174  	if got, want := len(log.Entries), 1; got != want {
   175  		t.Fatalf("len(log.Entries): got %d, want %d", got, want)
   176  	}
   177  
   178  	hres := log.Entries[0].Response
   179  	if got, want := hres.Status, 301; got != want {
   180  		t.Errorf("hres.Status: got %d, want %d", got, want)
   181  	}
   182  
   183  	if got, want := hres.StatusText, "Moved Permanently"; got != want {
   184  		t.Errorf("hres.StatusText: got %q, want %q", got, want)
   185  	}
   186  
   187  	if got, want := hres.HTTPVersion, "HTTP/1.1"; got != want {
   188  		t.Errorf("hres.HTTPVersion: got %q, want %q", got, want)
   189  	}
   190  
   191  	if got, want := hres.Content.Text, []byte("response body"); !bytes.Equal(got, want) {
   192  		t.Errorf("hres.Content.Text: got %q, want %q", got, want)
   193  	}
   194  
   195  	wantHeaders := http.Header{
   196  		"Response-Header": {"first", "second"},
   197  		"Set-Cookie":      {cookie.String()},
   198  		"Location":        {"google.com"},
   199  		"Content-Length":  {"13"},
   200  	}
   201  	if got := headersToHTTP(hres.Headers); !reflect.DeepEqual(got, wantHeaders) {
   202  		t.Errorf("headers:\ngot:\n%+v\nwant:\n%+v", got, wantHeaders)
   203  	}
   204  
   205  	if got, want := len(hres.Cookies), 1; got != want {
   206  		t.Fatalf("len(hres.Cookies): got %d, want %d", got, want)
   207  	}
   208  
   209  	hcookie := hres.Cookies[0]
   210  	if got, want := hcookie.Name, "response"; got != want {
   211  		t.Errorf("hcookie.Name: got %q, want %q", got, want)
   212  	}
   213  	if got, want := hcookie.Value, "cookie"; got != want {
   214  		t.Errorf("hcookie.Value: got %q, want %q", got, want)
   215  	}
   216  	if got, want := hcookie.Path, "/"; got != want {
   217  		t.Errorf("hcookie.Path: got %q, want %q", got, want)
   218  	}
   219  	if got, want := hcookie.Domain, "example.com"; got != want {
   220  		t.Errorf("hcookie.Domain: got %q, want %q", got, want)
   221  	}
   222  	if got, want := hcookie.Expires, expires; got.Equal(want) {
   223  		t.Errorf("hcookie.Expires: got %s, want %s", got, want)
   224  	}
   225  	if !hcookie.HTTPOnly {
   226  		t.Error("hcookie.HTTPOnly: got false, want true")
   227  	}
   228  	if !hcookie.Secure {
   229  		t.Error("hcookie.Secure: got false, want true")
   230  	}
   231  }
   232  
   233  func TestModifyRequestBodyURLEncoded(t *testing.T) {
   234  	logger := NewLogger()
   235  
   236  	body := strings.NewReader("first=true&second=false")
   237  	req, err := http.NewRequest("POST", "http://example.com", body)
   238  	if err != nil {
   239  		t.Fatalf("http.NewRequest(): got %v, want no error", err)
   240  	}
   241  	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
   242  
   243  	_, remove, err := martian.TestContext(req, nil, nil)
   244  	if err != nil {
   245  		t.Fatalf("martian.TestContext(): got %v, want no error", err)
   246  	}
   247  	defer remove()
   248  
   249  	if err := logger.ModifyRequest(req); err != nil {
   250  		t.Fatalf("ModifyRequest(): got %v, want no error", err)
   251  	}
   252  
   253  	log := logger.Export().Log
   254  	if got, want := len(log.Entries), 1; got != want {
   255  		t.Errorf("len(log.Entries): got %v, want %v", got, want)
   256  	}
   257  
   258  	pd := log.Entries[0].Request.PostData
   259  	if got, want := pd.MimeType, "application/x-www-form-urlencoded"; got != want {
   260  		t.Errorf("PostData.MimeType: got %v, want %v", got, want)
   261  	}
   262  
   263  	if got, want := len(pd.Params), 2; got != want {
   264  		t.Fatalf("len(PostData.Params): got %d, want %d", got, want)
   265  	}
   266  
   267  	for _, p := range pd.Params {
   268  		var want string
   269  		switch p.Name {
   270  		case "first":
   271  			want = "true"
   272  		case "second":
   273  			want = "false"
   274  		default:
   275  			t.Errorf("PostData.Params: got %q, want to not be present", p.Name)
   276  			continue
   277  		}
   278  
   279  		if got := p.Value; got != want {
   280  			t.Errorf("PostData.Params[%q]: got %q, want %q", p.Name, got, want)
   281  		}
   282  	}
   283  }
   284  
   285  func TestModifyRequestBodyArbitraryContentType(t *testing.T) {
   286  	logger := NewLogger()
   287  
   288  	body := "arbitrary binary data"
   289  	req, err := http.NewRequest("POST", "http://www.example.com", strings.NewReader(body))
   290  	if err != nil {
   291  		t.Fatalf("http.NewRequest(): got %v, want no error", err)
   292  	}
   293  
   294  	_, remove, err := martian.TestContext(req, nil, nil)
   295  	if err != nil {
   296  		t.Fatalf("martian.TestContext(): got %v, want no error", err)
   297  	}
   298  	defer remove()
   299  
   300  	if err := logger.ModifyRequest(req); err != nil {
   301  		t.Fatalf("ModifyRequest(): got %v, want no error", err)
   302  	}
   303  
   304  	log := logger.Export().Log
   305  	if got, want := len(log.Entries), 1; got != want {
   306  		t.Errorf("len(log.Entries): got %d, want %d", got, want)
   307  	}
   308  
   309  	pd := log.Entries[0].Request.PostData
   310  	if got, want := pd.MimeType, ""; got != want {
   311  		t.Errorf("PostData.MimeType: got %q, want %q", got, want)
   312  	}
   313  	if got, want := len(pd.Params), 0; got != want {
   314  		t.Errorf("len(PostData.Params): got %d, want %d", got, want)
   315  	}
   316  
   317  	if got, want := pd.Text, body; got != want {
   318  		t.Errorf("PostData.Text: got %q, want %q", got, want)
   319  	}
   320  }
   321  
   322  func TestModifyRequestBodyMultipart(t *testing.T) {
   323  	logger := NewLogger()
   324  
   325  	body := new(bytes.Buffer)
   326  	mpw := multipart.NewWriter(body)
   327  	mpw.SetBoundary("boundary")
   328  
   329  	if err := mpw.WriteField("key", "value"); err != nil {
   330  		t.Errorf("mpw.WriteField(): got %v, want no error", err)
   331  	}
   332  
   333  	w, err := mpw.CreateFormFile("file", "test.txt")
   334  	if _, err = w.Write([]byte("file contents")); err != nil {
   335  		t.Fatalf("Write(): got %v, want no error", err)
   336  	}
   337  	mpw.Close()
   338  
   339  	req, err := http.NewRequest("POST", "http://example.com", body)
   340  	if err != nil {
   341  		t.Fatalf("http.NewRequest(): got %v, want no error", err)
   342  	}
   343  	req.Header.Set("Content-Type", mpw.FormDataContentType())
   344  
   345  	_, remove, err := martian.TestContext(req, nil, nil)
   346  	if err != nil {
   347  		t.Fatalf("martian.TestContext(): got %v, want no error", err)
   348  	}
   349  	defer remove()
   350  
   351  	if err := logger.ModifyRequest(req); err != nil {
   352  		t.Fatalf("ModifyRequest(): got %v, want no error", err)
   353  	}
   354  
   355  	log := logger.Export().Log
   356  	if got, want := len(log.Entries), 1; got != want {
   357  		t.Fatalf("len(log.Entries): got %d, want %d", got, want)
   358  	}
   359  
   360  	pd := log.Entries[0].Request.PostData
   361  	if got, want := pd.MimeType, "multipart/form-data"; got != want {
   362  		t.Errorf("PostData.MimeType: got %q, want %q", got, want)
   363  	}
   364  	if got, want := len(pd.Params), 2; got != want {
   365  		t.Errorf("PostData.Params: got %d, want %d", got, want)
   366  	}
   367  
   368  	for _, p := range pd.Params {
   369  		var want Param
   370  
   371  		switch p.Name {
   372  		case "key":
   373  			want = Param{
   374  				Filename:    "",
   375  				ContentType: "",
   376  				Value:       "value",
   377  			}
   378  		case "file":
   379  			want = Param{
   380  				Filename:    "test.txt",
   381  				ContentType: "application/octet-stream",
   382  				Value:       "file contents",
   383  			}
   384  		default:
   385  			t.Errorf("pd.Params: got %q, want not to be present", p.Name)
   386  			continue
   387  		}
   388  
   389  		if got, want := p.Filename, want.Filename; got != want {
   390  			t.Errorf("p.Filename: got %q, want %q", got, want)
   391  		}
   392  		if got, want := p.ContentType, want.ContentType; got != want {
   393  			t.Errorf("p.ContentType: got %q, want %q", got, want)
   394  		}
   395  		if got, want := p.Value, want.Value; got != want {
   396  			t.Errorf("p.Value: got %q, want %q", got, want)
   397  		}
   398  	}
   399  }
   400  
   401  func TestModifyRequestErrorsOnDuplicateRequest(t *testing.T) {
   402  	logger := NewLogger()
   403  
   404  	req, err := http.NewRequest("POST", "http://example.com", nil)
   405  	if err != nil {
   406  		t.Fatalf("http.NewRequest(): got %v, want no error", err)
   407  	}
   408  
   409  	_, remove, err := martian.TestContext(req, nil, nil)
   410  	if err != nil {
   411  		t.Fatalf("martian.TestContext(): got %v, want no error", err)
   412  	}
   413  	defer remove()
   414  
   415  	if err := logger.ModifyRequest(req); err != nil {
   416  		t.Fatalf("ModifyRequest(): got %v, want no error", err)
   417  	}
   418  
   419  	if logger.ModifyRequest(req) == nil {
   420  		t.Fatalf("ModifyRequest(): was supposed to error")
   421  	}
   422  }
   423  
   424  func TestHARExportsTime(t *testing.T) {
   425  	logger := NewLogger()
   426  
   427  	req, err := http.NewRequest("GET", "http://example.com", nil)
   428  	if err != nil {
   429  		t.Fatalf("NewRequest(): got %v, want no error", err)
   430  	}
   431  
   432  	_, remove, err := martian.TestContext(req, nil, nil)
   433  	if err != nil {
   434  		t.Fatalf("martian.TestContext(): got %v, want no error", err)
   435  	}
   436  	defer remove()
   437  
   438  	if err := logger.ModifyRequest(req); err != nil {
   439  		t.Fatalf("ModifyRequest(): got %v, want no error", err)
   440  	}
   441  
   442  	// Simulate fast network round trip.
   443  	time.Sleep(10 * time.Millisecond)
   444  
   445  	res := proxyutil.NewResponse(200, nil, req)
   446  
   447  	if err := logger.ModifyResponse(res); err != nil {
   448  		t.Fatalf("ModifyResponse(): got %v, want no error", err)
   449  	}
   450  
   451  	log := logger.Export().Log
   452  	if got, want := len(log.Entries), 1; got != want {
   453  		t.Fatalf("len(log.Entries): got %v, want %v", got, want)
   454  	}
   455  
   456  	entry := log.Entries[0]
   457  	min, max := int64(10), int64(100)
   458  	if got := entry.Time; got < min || got > max {
   459  		t.Errorf("entry.Time: got %dms, want between %dms and %vms", got, min, max)
   460  	}
   461  }
   462  
   463  func TestReset(t *testing.T) {
   464  	logger := NewLogger()
   465  
   466  	req, err := http.NewRequest("GET", "http://example.com", nil)
   467  	if err != nil {
   468  		t.Fatalf("NewRequest(): got %v, want no error", err)
   469  	}
   470  
   471  	_, remove, err := martian.TestContext(req, nil, nil)
   472  	if err != nil {
   473  		t.Fatalf("martian.TestContext(): got %v, want no error", err)
   474  	}
   475  	defer remove()
   476  
   477  	if err := logger.ModifyRequest(req); err != nil {
   478  		t.Fatalf("ModifyRequest(): got %v, want no error", err)
   479  	}
   480  
   481  	log := logger.Export().Log
   482  	if got, want := len(log.Entries), 1; got != want {
   483  		t.Fatalf("len(log.Entries): got %d, want %d", got, want)
   484  	}
   485  
   486  	logger.Reset()
   487  
   488  	log = logger.Export().Log
   489  	if got, want := len(log.Entries), 0; got != want {
   490  		t.Errorf("len(log.Entries): got %d, want %d", got, want)
   491  	}
   492  }
   493  
   494  func TestExportSortsEntries(t *testing.T) {
   495  	logger := NewLogger()
   496  	count := 10
   497  
   498  	for i := 0; i < count; i++ {
   499  		req, err := http.NewRequest("GET", "http://example.com", nil)
   500  		if err != nil {
   501  			t.Fatalf("NewRequest(): got %v, want no error", err)
   502  		}
   503  
   504  		_, remove, err := martian.TestContext(req, nil, nil)
   505  		if err != nil {
   506  			t.Fatalf("martian.TestContext(): got %v, want no error", err)
   507  		}
   508  		defer remove()
   509  
   510  		if err := logger.ModifyRequest(req); err != nil {
   511  			t.Fatalf("ModifyRequest(): got %v, want no error", err)
   512  		}
   513  	}
   514  
   515  	log := logger.Export().Log
   516  
   517  	for i := 0; i < count-1; i++ {
   518  		first := log.Entries[i]
   519  		second := log.Entries[i+1]
   520  
   521  		if got, want := first.StartedDateTime, second.StartedDateTime; got.After(want) {
   522  			t.Errorf("entry.StartedDateTime: got %s, want to be before %s", got, want)
   523  		}
   524  	}
   525  }
   526  
   527  func TestExportIgnoresOrphanedResponse(t *testing.T) {
   528  	logger := NewLogger()
   529  
   530  	req, err := http.NewRequest("GET", "http://example.com", nil)
   531  	if err != nil {
   532  		t.Fatalf("http.NewRequest(): got %v, want no error", err)
   533  	}
   534  
   535  	_, remove, err := martian.TestContext(req, nil, nil)
   536  	if err != nil {
   537  		t.Fatalf("martian.TestContext(): got %v, want no error", err)
   538  	}
   539  	defer remove()
   540  
   541  	if err := logger.ModifyRequest(req); err != nil {
   542  		t.Fatalf("ModifyRequest(): got %v, want no error", err)
   543  	}
   544  
   545  	// Reset before the response comes back.
   546  	logger.Reset()
   547  
   548  	res := proxyutil.NewResponse(200, nil, req)
   549  	if err := logger.ModifyResponse(res); err != nil {
   550  		t.Fatalf("ModifyResponse(): got %v, want no error", err)
   551  	}
   552  
   553  	log := logger.Export().Log
   554  	if got, want := len(log.Entries), 0; got != want {
   555  		t.Errorf("len(log.Entries): got %d, want %d", got, want)
   556  	}
   557  }
   558  
   559  func TestExportAndResetResetsCompleteRequests(t *testing.T) {
   560  	logger := NewLogger()
   561  
   562  	req, err := http.NewRequest("GET", "http://example.com", nil)
   563  	if err != nil {
   564  		t.Fatalf("http.NewRequest(): got %v, want no error", err)
   565  	}
   566  
   567  	_, remove, err := martian.TestContext(req, nil, nil)
   568  	if err != nil {
   569  		t.Fatalf("martian.TestContext(): got %v, want no error", err)
   570  	}
   571  	defer remove()
   572  
   573  	if err := logger.ModifyRequest(req); err != nil {
   574  		t.Fatalf("ModifyRequest(): got %v, want no error", err)
   575  	}
   576  
   577  	res := proxyutil.NewResponse(200, nil, req)
   578  	if err := logger.ModifyResponse(res); err != nil {
   579  		t.Fatalf("ModifyResponse(): got %v, want no error", err)
   580  	}
   581  
   582  	logger.ExportAndReset()
   583  
   584  	log := logger.Export().Log
   585  	if got, want := len(log.Entries), 0; got != want {
   586  		t.Errorf("len(log.Entries): got %d, want %d", got, want)
   587  	}
   588  }
   589  
   590  func TestExportAndResetLeavesPendingRequests(t *testing.T) {
   591  	logger := NewLogger()
   592  
   593  	req, err := http.NewRequest("GET", "http://example.com", nil)
   594  	if err != nil {
   595  		t.Fatalf("http.NewRequest(): got %v, want no error", err)
   596  	}
   597  
   598  	_, remove, err := martian.TestContext(req, nil, nil)
   599  	if err != nil {
   600  		t.Fatalf("martian.TestContext(): got %v, want no error", err)
   601  	}
   602  	defer remove()
   603  
   604  	if err := logger.ModifyRequest(req); err != nil {
   605  		t.Fatalf("ModifyRequest(): got %v, want no error", err)
   606  	}
   607  
   608  	logger.ExportAndReset()
   609  
   610  	log := logger.Export().Log
   611  	if got, want := len(log.Entries), 1; got != want {
   612  		t.Errorf("len(log.Entries): got %d, want %d", got, want)
   613  	}
   614  }
   615  
   616  func TestExportAndResetExportsCompleteRequests(t *testing.T) {
   617  	logger := NewLogger()
   618  
   619  	req, err := http.NewRequest("GET", "http://example.com", nil)
   620  	if err != nil {
   621  		t.Fatalf("http.NewRequest(): got %v, want no error", err)
   622  	}
   623  
   624  	_, remove, err := martian.TestContext(req, nil, nil)
   625  	if err != nil {
   626  		t.Fatalf("martian.TestContext(): got %v, want no error", err)
   627  	}
   628  	defer remove()
   629  
   630  	if err := logger.ModifyRequest(req); err != nil {
   631  		t.Fatalf("ModifyRequest(): got %v, want no error", err)
   632  	}
   633  
   634  	res := proxyutil.NewResponse(200, nil, req)
   635  	if err := logger.ModifyResponse(res); err != nil {
   636  		t.Fatalf("ModifyResponse(): got %v, want no error", err)
   637  	}
   638  
   639  	log := logger.ExportAndReset().Log
   640  	if got, want := len(log.Entries), 1; got != want {
   641  		t.Errorf("len(log.Entries): got %d, want %d", got, want)
   642  	}
   643  }
   644  
   645  func TestExportAndResetExportsCompleteRequestsWithPendingLeft(t *testing.T) {
   646  	logger := NewLogger()
   647  
   648  	req, err := http.NewRequest("GET", "http://example.com", nil)
   649  	if err != nil {
   650  		t.Fatalf("http.NewRequest(): got %v, want no error", err)
   651  	}
   652  
   653  	_, remove, err := martian.TestContext(req, nil, nil)
   654  	if err != nil {
   655  		t.Fatalf("martian.TestContext(): got %v, want no error", err)
   656  	}
   657  	defer remove()
   658  
   659  	if err := logger.ModifyRequest(req); err != nil {
   660  		t.Fatalf("ModifyRequest(): got %v, want no error", err)
   661  	}
   662  
   663  	req, err = http.NewRequest("GET", "http://example.com", nil)
   664  	if err != nil {
   665  		t.Fatalf("http.NewRequest(): got %v, want no error", err)
   666  	}
   667  
   668  	_, remove, err = martian.TestContext(req, nil, nil)
   669  	if err != nil {
   670  		t.Fatalf("martian.TestContext(): got %v, want no error", err)
   671  	}
   672  	defer remove()
   673  
   674  	if err := logger.ModifyRequest(req); err != nil {
   675  		t.Fatalf("ModifyRequest(): got %v, want no error", err)
   676  	}
   677  
   678  	req, err = http.NewRequest("GET", "http://example.com", nil)
   679  	if err != nil {
   680  		t.Fatalf("http.NewRequest(): got %v, want no error", err)
   681  	}
   682  
   683  	_, remove, err = martian.TestContext(req, nil, nil)
   684  	if err != nil {
   685  		t.Fatalf("martian.TestContext(): got %v, want no error", err)
   686  	}
   687  	defer remove()
   688  
   689  	if err := logger.ModifyRequest(req); err != nil {
   690  		t.Fatalf("ModifyRequest(): got %v, want no error", err)
   691  	}
   692  
   693  	res := proxyutil.NewResponse(200, nil, req)
   694  	if err := logger.ModifyResponse(res); err != nil {
   695  		t.Fatalf("ModifyResponse(): got %v, want no error", err)
   696  	}
   697  
   698  	log := logger.ExportAndReset().Log
   699  	if got, want := len(log.Entries), 1; got != want {
   700  		t.Errorf("len(log.Entries): got %d, want %d", got, want)
   701  	}
   702  
   703  	log = logger.Export().Log
   704  	if got, want := len(log.Entries), 2; got != want {
   705  		t.Errorf("len(log.Entries): got %d, want %d", got, want)
   706  	}
   707  }
   708  
   709  func TestSkippingLogging(t *testing.T) {
   710  	req, err := http.NewRequest("GET", "http://example.com", nil)
   711  	if err != nil {
   712  		t.Fatalf("NewRequest(): got %v, want no error", err)
   713  	}
   714  
   715  	ctx, remove, err := martian.TestContext(req, nil, nil)
   716  	if err != nil {
   717  		t.Fatalf("martian.TestContext(): got %v, want no error", err)
   718  	}
   719  	defer remove()
   720  
   721  	ctx.SkipLogging()
   722  
   723  	logger := NewLogger()
   724  
   725  	if err := logger.ModifyRequest(req); err != nil {
   726  		t.Fatalf("ModifyRequest(): got %v, want no error", err)
   727  	}
   728  
   729  	res := proxyutil.NewResponse(200, nil, req)
   730  	if err := logger.ModifyResponse(res); err != nil {
   731  		t.Fatalf("ModifyResponse(): got %v, want no error", err)
   732  	}
   733  
   734  	log := logger.Export().Log
   735  	if got, want := len(log.Entries), 0; got != want {
   736  		t.Fatalf("len(log.Entries): got %d, want %d", got, want)
   737  	}
   738  }
   739  
   740  func TestOptionResponseBodyLogging(t *testing.T) {
   741  	req, err := http.NewRequest("GET", "http://example.com", nil)
   742  	if err != nil {
   743  		t.Fatalf("NewRequest(): got %v, want no error", err)
   744  	}
   745  
   746  	_, remove, err := martian.TestContext(req, nil, nil)
   747  	if err != nil {
   748  		t.Fatalf("martian.TestContext(): got %v, want no error", err)
   749  	}
   750  	defer remove()
   751  
   752  	bdr := strings.NewReader("{\"response\": \"body\"}")
   753  	res := proxyutil.NewResponse(200, bdr, req)
   754  	res.ContentLength = int64(bdr.Len())
   755  	res.Header.Set("Content-Type", "application/json")
   756  
   757  	logger := NewLogger()
   758  
   759  	if err := logger.ModifyRequest(req); err != nil {
   760  		t.Fatalf("ModifyRequest(): got %v, want no error", err)
   761  	}
   762  
   763  	if err := logger.ModifyResponse(res); err != nil {
   764  		t.Fatalf("ModifyResponse(): got %v, want no error", err)
   765  	}
   766  
   767  	log := logger.Export().Log
   768  	if got, want := len(log.Entries), 1; got != want {
   769  		t.Fatalf("len(log.Entries): got %d, want %d", got, want)
   770  	}
   771  
   772  	if got, want := string(log.Entries[0].Response.Content.Text), "{\"response\": \"body\"}"; got != want {
   773  		t.Fatalf("log.Entries[0].Response.Content.Text: got %s, want %s", got, want)
   774  	}
   775  
   776  	logger = NewLogger()
   777  	logger.SetOption(BodyLogging(false))
   778  
   779  	if err := logger.ModifyRequest(req); err != nil {
   780  		t.Fatalf("ModifyRequest(): got %v, want no error", err)
   781  	}
   782  
   783  	if err := logger.ModifyResponse(res); err != nil {
   784  		t.Fatalf("ModifyResponse(): got %v, want no error", err)
   785  	}
   786  
   787  	log = logger.Export().Log
   788  	if got, want := len(log.Entries), 1; got != want {
   789  		t.Fatalf("len(log.Entries): got %d, want %d", got, want)
   790  	}
   791  
   792  	if got, want := string(log.Entries[0].Response.Content.Text), ""; got != want {
   793  		t.Fatalf("log.Entries[0].Response.Content: got %s, want %s", got, want)
   794  	}
   795  
   796  	logger = NewLogger()
   797  	logger.SetOption(BodyLoggingForContentTypes("application/json"))
   798  
   799  	if err := logger.ModifyRequest(req); err != nil {
   800  		t.Fatalf("ModifyRequest(): got %v, want no error", err)
   801  	}
   802  
   803  	if err := logger.ModifyResponse(res); err != nil {
   804  		t.Fatalf("ModifyResponse(): got %v, want no error", err)
   805  	}
   806  
   807  	log = logger.Export().Log
   808  	if got, want := string(log.Entries[0].Response.Content.Text), "{\"response\": \"body\"}"; got != want {
   809  		t.Fatalf("log.Entries[0].Response.Content: got %s, want %s", got, want)
   810  	}
   811  
   812  	logger = NewLogger()
   813  	logger.SetOption(SkipBodyLoggingForContentTypes("application/json"))
   814  
   815  	if err := logger.ModifyRequest(req); err != nil {
   816  		t.Fatalf("ModifyRequest(): got %v, want no error", err)
   817  	}
   818  
   819  	if err := logger.ModifyResponse(res); err != nil {
   820  		t.Fatalf("ModifyResponse(): got %v, want no error", err)
   821  	}
   822  
   823  	log = logger.Export().Log
   824  	if got, want := string(log.Entries[0].Response.Content.Text), ""; got != want {
   825  		t.Fatalf("log.Entries[0].Response.Content: got %v, want %v", got, want)
   826  	}
   827  }
   828  
   829  func TestOptionRequestPostDataLogging(t *testing.T) {
   830  	logger := NewLogger()
   831  	logger.SetOption(PostDataLoggingForContentTypes("application/x-www-form-urlencoded"))
   832  
   833  	body := strings.NewReader("first=true&second=false")
   834  	req, err := http.NewRequest("POST", "http://example.com", body)
   835  	if err != nil {
   836  		t.Fatalf("http.NewRequest(): got %v, want no error", err)
   837  	}
   838  	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
   839  
   840  	_, remove, err := martian.TestContext(req, nil, nil)
   841  	if err != nil {
   842  		t.Fatalf("martian.TestContext(): got %v, want no error", err)
   843  	}
   844  	defer remove()
   845  
   846  	if err := logger.ModifyRequest(req); err != nil {
   847  		t.Fatalf("ModifyRequest(): got %v, want no error", err)
   848  	}
   849  
   850  	log := logger.Export().Log
   851  
   852  	for _, param := range log.Entries[0].Request.PostData.Params {
   853  		if param.Name == "first" {
   854  			if got, want := param.Value, "true"; got != want {
   855  				t.Fatalf("Params[%q].Value: got %s, want %s", param.Name, got, want)
   856  			}
   857  		}
   858  
   859  		if param.Name == "second" {
   860  			if got, want := param.Value, "false"; got != want {
   861  				t.Fatalf("Params[%q].Value: got %s, want %s", param.Name, got, want)
   862  			}
   863  		}
   864  	}
   865  
   866  	logger = NewLogger()
   867  	logger.SetOption(SkipPostDataLoggingForContentTypes("application/x-www-form-urlencoded"))
   868  
   869  	body = strings.NewReader("first=true&second=false")
   870  	req, err = http.NewRequest("POST", "http://example.com", body)
   871  	if err != nil {
   872  		t.Fatalf("http.NewRequest(): got %v, want no error", err)
   873  	}
   874  	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
   875  
   876  	_, remove, err = martian.TestContext(req, nil, nil)
   877  	if err != nil {
   878  		t.Fatalf("martian.TestContext(): got %v, want no error", err)
   879  	}
   880  	defer remove()
   881  
   882  	if err := logger.ModifyRequest(req); err != nil {
   883  		t.Fatalf("ModifyRequest(): got %v, want no error", err)
   884  	}
   885  
   886  	log = logger.Export().Log
   887  	if got, want := len(log.Entries[0].Request.PostData.Params), 0; got != want {
   888  		t.Fatalf("len(log.Entries[0].Request.PostData.Params): got %v, want %v", got, want)
   889  	}
   890  }
   891  
   892  func TestJSONMarshalPostData(t *testing.T) {
   893  	// Verify that encoding/json round-trips har.PostData with both text and binary data.
   894  	for _, text := range []string{"hello", string([]byte{150, 151, 152})} {
   895  		want := &PostData{
   896  			MimeType: "m",
   897  			Params:   []Param{{Name: "n", Value: "v"}},
   898  			Text:     text,
   899  		}
   900  		data, err := json.Marshal(want)
   901  		if err != nil {
   902  			t.Fatal(err)
   903  		}
   904  		var got PostData
   905  		if err := json.Unmarshal(data, &got); err != nil {
   906  			t.Fatal(err)
   907  		}
   908  		if !reflect.DeepEqual(&got, want) {
   909  			t.Errorf("got %+v, want %+v", &got, want)
   910  		}
   911  	}
   912  }
   913  
   914  func TestJSONMarshalContent(t *testing.T) {
   915  	testCases := []struct {
   916  		name     string
   917  		text     []byte
   918  		encoding string
   919  	}{
   920  		{
   921  			name:     "binary data with base64 encoding",
   922  			text:     []byte{120, 31, 99, 3},
   923  			encoding: "base64",
   924  		},
   925  		{
   926  			name: "ascii data with no encoding",
   927  			text: []byte("hello martian"),
   928  		},
   929  		{
   930  			name:     "ascii data with base64 encoding",
   931  			text:     []byte("hello martian"),
   932  			encoding: "base64",
   933  		},
   934  	}
   935  
   936  	for _, c := range testCases {
   937  		want := Content{
   938  			Size:     int64(len(c.text)),
   939  			MimeType: "application/x-test",
   940  			Text:     c.text,
   941  			Encoding: c.encoding,
   942  		}
   943  		data, err := json.Marshal(want)
   944  		if err != nil {
   945  			t.Fatal(err)
   946  		}
   947  		var got Content
   948  		if err := json.Unmarshal(data, &got); err != nil {
   949  			t.Fatal(err)
   950  		}
   951  		if !reflect.DeepEqual(got, want) {
   952  			t.Errorf("got %+v, want %+v", got, want)
   953  		}
   954  	}
   955  }