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.
 
 
 

406 lines
11 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 marbl
  15. import (
  16. "bytes"
  17. "io"
  18. "net/http"
  19. "strconv"
  20. "strings"
  21. "testing"
  22. "time"
  23. "github.com/google/martian"
  24. "github.com/google/martian/proxyutil"
  25. )
  26. func TestMarkAPIRequestsWithHeader(t *testing.T) {
  27. areq, err := http.NewRequest("POST", "http://localhost:8080/configure", nil)
  28. if err != nil {
  29. t.Fatalf("http.NewRequest(): got %v, want no error", err)
  30. }
  31. ctx, remove, err := martian.TestContext(areq, nil, nil)
  32. if err != nil {
  33. t.Fatalf("TestContext(): got %v, want no error", err)
  34. }
  35. defer remove()
  36. ctx.APIRequest()
  37. req, err := http.NewRequest("GET", "http://example.com", nil)
  38. if err != nil {
  39. t.Fatalf("http.NewRequest(): got %v, want no error", err)
  40. }
  41. _, removereq, err := martian.TestContext(req, nil, nil)
  42. if err != nil {
  43. t.Fatalf("TestContext(): got %v, want no error", err)
  44. }
  45. defer removereq()
  46. var b bytes.Buffer
  47. s := NewStream(&b)
  48. s.LogRequest("00000000", areq)
  49. s.LogRequest("00000001", req)
  50. s.Close()
  51. headers := make(map[string]string)
  52. reader := NewReader(&b)
  53. for {
  54. frame, err := reader.ReadFrame()
  55. if frame == nil {
  56. break
  57. }
  58. if err != nil && err != io.EOF {
  59. t.Fatalf("reader.ReadFrame(): got %v, want no error or io.EOF", err)
  60. }
  61. headerFrame, ok := frame.(Header)
  62. if !ok {
  63. t.Fatalf("frame.(Header): couldn't convert frame '%v' to a headerFrame", frame)
  64. }
  65. headers[headerFrame.ID+headerFrame.Name] = headerFrame.Value
  66. }
  67. apih, ok := headers["00000000:api"]
  68. if !ok {
  69. t.Errorf("headers[00000000:api]: got no such header, want :api (headers were: %v)", headers)
  70. }
  71. if got, want := apih, "true"; got != want {
  72. t.Errorf("headers[%q]: got %v, want %q", "00000000:api", got, want)
  73. }
  74. _, ok = headers["00000001:api"]
  75. if got, want := ok, false; got != want {
  76. t.Error("headers[00000001:api]: got :api header, want no header for non-api requests")
  77. }
  78. }
  79. func TestSendTimestampWithLogRequest(t *testing.T) {
  80. req, err := http.NewRequest("POST", "http://example.com", nil)
  81. if err != nil {
  82. t.Fatalf("http.NewRequest(): got %v, want no error", err)
  83. }
  84. _, remove, err := martian.TestContext(req, nil, nil)
  85. if err != nil {
  86. t.Fatalf("TestContext(): got %v, want no error", err)
  87. }
  88. defer remove()
  89. var b bytes.Buffer
  90. s := NewStream(&b)
  91. before := time.Now().UnixNano() / 1000 / 1000
  92. s.LogRequest("Fake_Id0", req)
  93. s.Close()
  94. after := time.Now().UnixNano() / 1000 / 1000
  95. headers := make(map[string]string)
  96. reader := NewReader(&b)
  97. for {
  98. frame, err := reader.ReadFrame()
  99. if frame == nil {
  100. break
  101. }
  102. if err != nil && err != io.EOF {
  103. t.Fatalf("reader.ReadFrame(): got %v, want no error or io.EOF", err)
  104. }
  105. headerFrame, ok := frame.(Header)
  106. if !ok {
  107. t.Fatalf("frame.(Header): couldn't convert frame '%v' to a headerFrame", frame)
  108. }
  109. headers[headerFrame.Name] = headerFrame.Value
  110. }
  111. timestr, ok := headers[":timestamp"]
  112. if !ok {
  113. t.Fatalf("headers[:timestamp]: got no such header, want :timestamp (headers were: %v)", headers)
  114. }
  115. ts, err := strconv.ParseInt(timestr, 10, 64)
  116. if err != nil {
  117. t.Fatalf("strconv.ParseInt: got %s, want no error. Invalidly formatted timestamp ('%s')", err, timestr)
  118. }
  119. if ts < before || ts > after {
  120. t.Fatalf("headers[:timestamp]: got %d, want timestamp between %d and %d", ts, before, after)
  121. }
  122. }
  123. func TestSendTimestampWithLogResponse(t *testing.T) {
  124. req, err := http.NewRequest("POST", "http://example.com", nil)
  125. if err != nil {
  126. t.Fatalf("http.NewRequest(): got %v, want no error", err)
  127. }
  128. _, remove, err := martian.TestContext(req, nil, nil)
  129. if err != nil {
  130. t.Fatalf("TestContext(): got %v, want no error", err)
  131. }
  132. defer remove()
  133. res := proxyutil.NewResponse(200, nil, req)
  134. var b bytes.Buffer
  135. s := NewStream(&b)
  136. before := time.Now().UnixNano() / 1000 / 1000
  137. s.LogResponse("Fake_Id1", res)
  138. s.Close()
  139. after := time.Now().UnixNano() / 1000 / 1000
  140. headers := make(map[string]string)
  141. reader := NewReader(&b)
  142. for {
  143. frame, err := reader.ReadFrame()
  144. if frame == nil {
  145. break
  146. }
  147. if err != nil && err != io.EOF {
  148. t.Fatalf("reader.ReadFrame(): got %v, want no error or io.EOF", err)
  149. }
  150. headerFrame, ok := frame.(Header)
  151. if !ok {
  152. t.Fatalf("frame.(Header): couldn't convert frame '%v' to a headerFrame", frame)
  153. }
  154. headers[headerFrame.Name] = headerFrame.Value
  155. }
  156. timestr, ok := headers[":timestamp"]
  157. if !ok {
  158. t.Fatalf("headers[:timestamp]: got no such header, want :timestamp (headers were: %v)", headers)
  159. }
  160. ts, err := strconv.ParseInt(timestr, 10, 64)
  161. if err != nil {
  162. t.Fatalf("strconv.ParseInt: got %s, want no error. Invalidly formatted timestamp ('%s')", err, timestr)
  163. }
  164. if ts < before || ts > after {
  165. t.Fatalf("headers[:timestamp]: got %d, want timestamp between %d and %d (headers were: %v)", ts, before, after, headers)
  166. }
  167. }
  168. func TestBodyLoggingWithOneRead(t *testing.T) {
  169. // Test scenario:
  170. // 1. Prepare HTTP request with body containing a string.
  171. // 2. Initialize marbl logging on this request.
  172. // 3. Read body of the request in single Read() and verity that it matches
  173. // original string.
  174. // 4. Parse marbl data, extract DataFrames and verify that they match
  175. // . original string.
  176. body := "hello, world"
  177. req, err := http.NewRequest("POST", "http://example.com", strings.NewReader(body))
  178. if err != nil {
  179. t.Fatalf("http.NewRequest(): got %v, want no error", err)
  180. }
  181. _, remove, err := martian.TestContext(req, nil, nil)
  182. if err != nil {
  183. t.Fatalf("TestContext(): got %v, want no error", err)
  184. }
  185. defer remove()
  186. var b bytes.Buffer
  187. s := NewStream(&b)
  188. s.LogRequest("Fake_Id0", req)
  189. // Read request body into big slice.
  190. bodybytes := make([]byte, 100)
  191. // First read. Due to implementation details of strings.Read
  192. // it reads all bytes but doesn't return EOF.
  193. n, err := req.Body.Read(bodybytes)
  194. if n != len(body) {
  195. t.Fatalf("req.Body.Read(): expected to read %v bytes but read %v", len(body), n)
  196. }
  197. if body != string(bodybytes[:n]) {
  198. t.Fatalf("req.Body.Read(): expected to read %v but read %v", body, string(bodybytes[:n]))
  199. }
  200. if err != nil {
  201. t.Fatalf("req.Body.Read(): first read expected to be successful but got error %v", err)
  202. }
  203. // second read. We already consumed the whole string on the first read
  204. // so now it should be 0 bytes and EOF.
  205. n, err = req.Body.Read(bodybytes)
  206. if n != 0 {
  207. t.Fatalf("req.Body.Read(): expected to read 0 bytes but read %v", n)
  208. }
  209. if err != io.EOF {
  210. t.Fatalf("req.Body.Read(): expected EOF but got %v", err)
  211. }
  212. s.Close()
  213. reader := NewReader(&b)
  214. bodybytes = readAllDataFrames(reader, "Fake_Id0", t)
  215. if len(bodybytes) != len(body) {
  216. t.Fatalf("readAllDataFrames(): expected .marbl data to have %v bytes, but got %v", len(body), len(bodybytes))
  217. }
  218. if body != string(bodybytes) {
  219. t.Fatalf("readAllDataFrames(): expected .marbl data to have string %v but got %v", body, string(bodybytes))
  220. }
  221. }
  222. func TestBodyLogging_ManyReads(t *testing.T) {
  223. // Test scenario:
  224. // 1. Prepare HTTP request with body containing a string.
  225. // 2. Initialize marbl logging on this request.
  226. // 3. Read body of the request in many reads, 1 byte per read and
  227. // . verify that it matches original string.
  228. // 4. Parse marbl data, extract DataFrames and verify that they match
  229. // . original string.
  230. body := "hello, world"
  231. req, err := http.NewRequest("POST", "http://example.com", strings.NewReader(body))
  232. if err != nil {
  233. t.Fatalf("http.NewRequest(): got %v, want no error", err)
  234. }
  235. _, remove, err := martian.TestContext(req, nil, nil)
  236. if err != nil {
  237. t.Fatalf("TestContext(): got %v, want no error", err)
  238. }
  239. defer remove()
  240. var b bytes.Buffer
  241. s := NewStream(&b)
  242. s.LogRequest("Fake_Id0", req)
  243. // Read request body into single byte slice.
  244. bodybytes := make([]byte, 1)
  245. for i := 0; i < len(body); i++ {
  246. // first read
  247. n, err := req.Body.Read(bodybytes)
  248. if n != 1 {
  249. t.Fatalf("req.Body.Read(): expected to read 1 byte but read %v", n)
  250. }
  251. if body[i] != bodybytes[0] {
  252. t.Fatalf("req.Body.Read(): expected to read %v but read %v", body[i], bodybytes[0])
  253. }
  254. if err != nil {
  255. t.Fatalf("req.Body.Read(): read expected to be successfully but got error %v", err)
  256. }
  257. }
  258. // last read. We already consumed the whole string on the previous reads
  259. // so now it should be 0 bytes and EOF.
  260. n, err := req.Body.Read(bodybytes)
  261. if n != 0 {
  262. t.Fatalf("req.Body.Read(): expected to read 0 bytes but read %v", n)
  263. }
  264. if err != io.EOF {
  265. t.Fatalf("req.Body.Read(): expected EOF but got %v", err)
  266. }
  267. s.Close()
  268. reader := NewReader(&b)
  269. bodybytes = readAllDataFrames(reader, "Fake_Id0", t)
  270. if len(bodybytes) != len(body) {
  271. t.Fatalf("readAllDataFrames(): expected .marbl data to have %v bytes, but got %v", len(body), len(bodybytes))
  272. }
  273. if body != string(bodybytes) {
  274. t.Fatalf("readAllDataFrames(): expected .marbl data to have string %v but got %v", body, string(bodybytes))
  275. }
  276. }
  277. func TestReturnOriginalRequestPathAndQuery(t *testing.T) {
  278. req, err := http.NewRequest("GET", "http://example.com/foo%20bar?baz%20qux", nil)
  279. if err != nil {
  280. t.Fatalf("http.NewRequest(): got %v, want no error", err)
  281. }
  282. _, remove, err := martian.TestContext(req, nil, nil)
  283. if err != nil {
  284. t.Fatalf("TestContext(): got %v, want no error", err)
  285. }
  286. defer remove()
  287. var b bytes.Buffer
  288. s := NewStream(&b)
  289. s.LogRequest("Fake_Id0", req)
  290. s.Close()
  291. headers := make(map[string]string)
  292. reader := NewReader(&b)
  293. for {
  294. frame, err := reader.ReadFrame()
  295. if frame == nil {
  296. break
  297. }
  298. if err != nil && err != io.EOF {
  299. t.Fatalf("reader.ReadFrame(): got %v, want no error or io.EOF", err)
  300. }
  301. headerFrame, ok := frame.(Header)
  302. if !ok {
  303. t.Fatalf("frame.(Header): couldn't convert frame '%v' to a headerFrame", frame)
  304. }
  305. headers[headerFrame.Name] = headerFrame.Value
  306. }
  307. path := headers[":path"]
  308. if path != "/foo%20bar" {
  309. t.Fatalf("headers[:path]: expected /foo%%20bar but got %s", path)
  310. }
  311. query := headers[":query"]
  312. if query != "baz%20qux" {
  313. t.Fatalf("headers[:query]: expected baz%%20qux but got %s", query)
  314. }
  315. }
  316. // readAllDataFrames reads all DataFrames with reader, filters the one that match provided
  317. // id and assembles data from all frames into single slice. It expects that
  318. // there is only one slice of DataFrames with provided id.
  319. func readAllDataFrames(reader *Reader, id string, t *testing.T) []byte {
  320. res := make([]byte, 0)
  321. term := false
  322. var i uint32
  323. for {
  324. frame, _ := reader.ReadFrame()
  325. if frame == nil {
  326. break
  327. }
  328. if frame.FrameType() == DataFrame {
  329. df := frame.(Data)
  330. if df.ID != id {
  331. continue
  332. }
  333. if term {
  334. t.Fatal("DataFrame after terminal frame are not allowed.")
  335. }
  336. if df.Index != i {
  337. t.Fatalf("expected DataFrame index %v but got %v", i, df.Index)
  338. }
  339. term = df.Terminal
  340. res = append(res, df.Data...)
  341. i++
  342. }
  343. }
  344. if !term {
  345. t.Fatal("didn't see terminal DataFrame")
  346. }
  347. return res
  348. }