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.
 
 
 

913 lines
25 KiB

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