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.
 
 
 

476 lines
14 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 static
  15. import (
  16. "bytes"
  17. "fmt"
  18. "io/ioutil"
  19. "net/http"
  20. "os"
  21. "path"
  22. "testing"
  23. "github.com/google/martian"
  24. "github.com/google/martian/parse"
  25. "github.com/google/martian/proxyutil"
  26. )
  27. func Test404WhenExplictlyMappedFileDoesNotExist(t *testing.T) {
  28. tmpdir, err := ioutil.TempDir("", "test_static_modifier_explicit_path_mapping_")
  29. if err != nil {
  30. t.Fatalf("ioutil.TempDir(): got %v, want no error", err)
  31. }
  32. //if err := os.MkdirAll(path.Join(tmpdir, "explicit/path"), 0777); err != nil {
  33. // t.Fatalf("os.Mkdir(): got %v, want no error", err)
  34. //}
  35. //if err := ioutil.WriteFile(path.Join(tmpdir, "explicit/path", "sfmtest.txt"), []byte("test file"), 0777); err != nil {
  36. // t.Fatalf("ioutil.WriteFile(): got %v, want no error", err)
  37. //}
  38. req, err := http.NewRequest("GET", "/sfmtest.txt", nil)
  39. if err != nil {
  40. t.Fatalf("NewRequest(): got %v, want no error", err)
  41. }
  42. _, remove, err := martian.TestContext(req, nil, nil)
  43. if err != nil {
  44. t.Fatalf("TestContext(): got %v, want no error", err)
  45. }
  46. defer remove()
  47. res := proxyutil.NewResponse(http.StatusOK, nil, req)
  48. mod := NewModifier(tmpdir)
  49. if err := mod.ModifyRequest(req); err != nil {
  50. t.Fatalf("ModifyRequest(): got %v, want no error", err)
  51. }
  52. mod.SetExplicitPathMappings(map[string]string{"/sfmtest.txt": "/explicit/path/sfmtest.txt"})
  53. if err := mod.ModifyResponse(res); err != nil {
  54. t.Fatalf("ModifyResponse(): got %v, want no error", err)
  55. }
  56. if got, want := res.StatusCode, http.StatusNotFound; got != want {
  57. t.Errorf("res.StatusCode: got %v, want %v", got, want)
  58. }
  59. }
  60. func TestFileExistsInBothExplictlyMappedPathAndInferredPath(t *testing.T) {
  61. tmpdir, err := ioutil.TempDir("", "test_static_modifier_explicit_path_mapping_")
  62. if err != nil {
  63. t.Fatalf("ioutil.TempDir(): got %v, want no error", err)
  64. }
  65. if err := os.MkdirAll(path.Join(tmpdir, "explicit/path"), 0777); err != nil {
  66. t.Fatalf("os.Mkdir(): got %v, want no error", err)
  67. }
  68. if err := ioutil.WriteFile(path.Join(tmpdir, "sfmtest.txt"), []byte("dont return"), 0777); err != nil {
  69. t.Fatalf("ioutil.WriteFile(): got %v, want no error", err)
  70. }
  71. if err := ioutil.WriteFile(path.Join(tmpdir, "explicit/path", "sfmtest.txt"), []byte("target"), 0777); err != nil {
  72. t.Fatalf("ioutil.WriteFile(): got %v, want no error", err)
  73. }
  74. req, err := http.NewRequest("GET", "/sfmtest.txt", nil)
  75. if err != nil {
  76. t.Fatalf("NewRequest(): got %v, want no error", err)
  77. }
  78. _, remove, err := martian.TestContext(req, nil, nil)
  79. if err != nil {
  80. t.Fatalf("TestContext(): got %v, want no error", err)
  81. }
  82. defer remove()
  83. res := proxyutil.NewResponse(http.StatusOK, nil, req)
  84. mod := NewModifier(tmpdir)
  85. if err := mod.ModifyRequest(req); err != nil {
  86. t.Fatalf("ModifyRequest(): got %v, want no error", err)
  87. }
  88. mod.SetExplicitPathMappings(map[string]string{"/sfmtest.txt": "/explicit/path/sfmtest.txt"})
  89. if err := mod.ModifyResponse(res); err != nil {
  90. t.Fatalf("ModifyResponse(): got %v, want no error", err)
  91. }
  92. if got, want := res.Header.Get("Content-Type"), "text/plain; charset=utf-8"; got != want {
  93. t.Errorf("res.Header.Get('Content-Type'): got %v, want %v", got, want)
  94. }
  95. got, err := ioutil.ReadAll(res.Body)
  96. if err != nil {
  97. t.Fatalf("ioutil.ReadAll(): got %v, want no error", err)
  98. }
  99. res.Body.Close()
  100. if want := []byte("target"); !bytes.Equal(got, want) {
  101. t.Errorf("res.Body: got %q, want %q", got, want)
  102. }
  103. if got, want := res.ContentLength, int64(len("target")); got != want {
  104. t.Errorf("res.ContentLength: got %v, want %v", got, want)
  105. }
  106. }
  107. func TestStaticModifierExplicitPathMapping(t *testing.T) {
  108. tmpdir, err := ioutil.TempDir("", "test_static_modifier_explicit_path_mapping_")
  109. if err != nil {
  110. t.Fatalf("ioutil.TempDir(): got %v, want no error", err)
  111. }
  112. if err := os.MkdirAll(path.Join(tmpdir, "explicit/path"), 0777); err != nil {
  113. t.Fatalf("os.Mkdir(): got %v, want no error", err)
  114. }
  115. if err := ioutil.WriteFile(path.Join(tmpdir, "explicit/path", "sfmtest.txt"), []byte("test file"), 0777); err != nil {
  116. t.Fatalf("ioutil.WriteFile(): got %v, want no error", err)
  117. }
  118. req, err := http.NewRequest("GET", "/sfmtest.txt", nil)
  119. if err != nil {
  120. t.Fatalf("NewRequest(): got %v, want no error", err)
  121. }
  122. _, remove, err := martian.TestContext(req, nil, nil)
  123. if err != nil {
  124. t.Fatalf("TestContext(): got %v, want no error", err)
  125. }
  126. defer remove()
  127. res := proxyutil.NewResponse(http.StatusOK, nil, req)
  128. mod := NewModifier(tmpdir)
  129. if err := mod.ModifyRequest(req); err != nil {
  130. t.Fatalf("ModifyRequest(): got %v, want no error", err)
  131. }
  132. mod.SetExplicitPathMappings(map[string]string{"/sfmtest.txt": "/explicit/path/sfmtest.txt"})
  133. if err := mod.ModifyResponse(res); err != nil {
  134. t.Fatalf("ModifyResponse(): got %v, want no error", err)
  135. }
  136. if got, want := res.Header.Get("Content-Type"), "text/plain; charset=utf-8"; got != want {
  137. t.Errorf("res.Header.Get('Content-Type'): got %v, want %v", got, want)
  138. }
  139. got, err := ioutil.ReadAll(res.Body)
  140. if err != nil {
  141. t.Fatalf("ioutil.ReadAll(): got %v, want no error", err)
  142. }
  143. res.Body.Close()
  144. if want := []byte("test file"); !bytes.Equal(got, want) {
  145. t.Errorf("res.Body: got %q, want %q", got, want)
  146. }
  147. if got, want := res.ContentLength, int64(len("test file")); got != want {
  148. t.Errorf("res.ContentLength: got %v, want %v", got, want)
  149. }
  150. }
  151. func TestStaticModifierOnRequest(t *testing.T) {
  152. tmpdir, err := ioutil.TempDir("", "test_static_modifier_on_request_")
  153. if err != nil {
  154. t.Fatalf("ioutil.TempDir(): got %v, want no error", err)
  155. }
  156. if err := ioutil.WriteFile(path.Join(tmpdir, "sfmtest.txt"), []byte("test file"), 0777); err != nil {
  157. t.Fatalf("ioutil.WriteFile(): got %v, want no error", err)
  158. }
  159. req, err := http.NewRequest("GET", "/sfmtest.txt", nil)
  160. if err != nil {
  161. t.Fatalf("NewRequest(): got %v, want no error", err)
  162. }
  163. _, remove, err := martian.TestContext(req, nil, nil)
  164. if err != nil {
  165. t.Fatalf("TestContext(): got %v, want no error", err)
  166. }
  167. defer remove()
  168. res := proxyutil.NewResponse(http.StatusOK, nil, req)
  169. mod := NewModifier(tmpdir)
  170. if err := mod.ModifyRequest(req); err != nil {
  171. t.Fatalf("ModifyRequest(): got %v, want no error", err)
  172. }
  173. if err := mod.ModifyResponse(res); err != nil {
  174. t.Fatalf("ModifyResponse(): got %v, want no error", err)
  175. }
  176. if got, want := res.Header.Get("Content-Type"), "text/plain; charset=utf-8"; got != want {
  177. t.Errorf("res.Header.Get('Content-Type'): got %v, want %v", got, want)
  178. }
  179. got, err := ioutil.ReadAll(res.Body)
  180. if err != nil {
  181. t.Fatalf("ioutil.ReadAll(): got %v, want no error", err)
  182. }
  183. res.Body.Close()
  184. if want := []byte("test file"); !bytes.Equal(got, want) {
  185. t.Errorf("res.Body: got %q, want %q", got, want)
  186. }
  187. if got, want := res.ContentLength, int64(len("test file")); got != want {
  188. t.Errorf("res.ContentLength: got %v, want %v", got, want)
  189. }
  190. }
  191. func TestRequestOverHTTPS(t *testing.T) {
  192. tmpdir, err := ioutil.TempDir("", "test_static_modifier_on_request_")
  193. if err != nil {
  194. t.Fatalf("ioutil.TempDir(): got %v, want no error", err)
  195. }
  196. if err := ioutil.WriteFile(path.Join(tmpdir, "sfmtest.txt"), []byte("test file"), 0777); err != nil {
  197. t.Fatalf("ioutil.WriteFile(): got %v, want no error", err)
  198. }
  199. req, err := http.NewRequest("GET", "/sfmtest.txt", nil)
  200. if err != nil {
  201. t.Fatalf("NewRequest(): got %v, want no error", err)
  202. }
  203. req.URL.Scheme = "https"
  204. _, remove, err := martian.TestContext(req, nil, nil)
  205. if err != nil {
  206. t.Fatalf("TestContext(): got %v, want no error", err)
  207. }
  208. defer remove()
  209. res := proxyutil.NewResponse(http.StatusOK, nil, req)
  210. mod := NewModifier(tmpdir)
  211. if err := mod.ModifyRequest(req); err != nil {
  212. t.Fatalf("ModifyRequest(): got %v, want no error", err)
  213. }
  214. if err := mod.ModifyResponse(res); err != nil {
  215. t.Fatalf("ModifyResponse(): got %v, want no error", err)
  216. }
  217. if got, want := res.Header.Get("Content-Type"), "text/plain; charset=utf-8"; got != want {
  218. t.Errorf("res.Header.Get('Content-Type'): got %v, want %v", got, want)
  219. }
  220. got, err := ioutil.ReadAll(res.Body)
  221. if err != nil {
  222. t.Fatalf("ioutil.ReadAll(): got %v, want no error", err)
  223. }
  224. res.Body.Close()
  225. if want := []byte("test file"); !bytes.Equal(got, want) {
  226. t.Errorf("res.Body: got %q, want %q", got, want)
  227. }
  228. if got, want := res.ContentLength, int64(len("test file")); got != want {
  229. t.Errorf("res.ContentLength: got %v, want %v", got, want)
  230. }
  231. }
  232. func TestModifierFromJSON(t *testing.T) {
  233. tmpdir, err := ioutil.TempDir("", "test_static_modifier_on_request_")
  234. if err != nil {
  235. t.Fatalf("ioutil.TempDir(): got %v, want no error", err)
  236. }
  237. tmpdir2 := path.Join(tmpdir, "subdir")
  238. err = os.Mkdir(tmpdir2, 0777)
  239. if err != nil {
  240. t.Fatalf("ioutil.TempDir(): got %v, want no error", err)
  241. }
  242. if err := ioutil.WriteFile(path.Join(tmpdir, "sfmtest.txt"), []byte("test file"), 0777); err != nil {
  243. t.Fatalf("ioutil.WriteFile(): got %v, want no error", err)
  244. }
  245. if err := ioutil.WriteFile(path.Join(tmpdir2, "sfmtest.txt"), []byte("test file2"), 0777); err != nil {
  246. t.Fatalf("ioutil.WriteFile(): got %v, want no error", err)
  247. }
  248. msg := []byte(fmt.Sprintf(`{
  249. "static.Modifier": {
  250. "scope": ["request", "response"],
  251. "explicitPaths": {"/foo/bar.baz": "/subdir/sfmtest.txt"},
  252. "rootPath": %q
  253. }
  254. }`, tmpdir))
  255. r, err := parse.FromJSON(msg)
  256. if err != nil {
  257. t.Fatalf("parse.FromJSON(): got %v, want no error", err)
  258. }
  259. reqmod := r.RequestModifier()
  260. if reqmod == nil {
  261. t.Fatal("reqmod: got nil, want not nil")
  262. }
  263. resmod := r.ResponseModifier()
  264. if resmod == nil {
  265. t.Fatal("resmod: got nil, want not nil")
  266. }
  267. req, err := http.NewRequest("GET", "/sfmtest.txt", nil)
  268. if err != nil {
  269. t.Fatalf("NewRequest(): got %v, want no error", err)
  270. }
  271. _, remove, err := martian.TestContext(req, nil, nil)
  272. if err != nil {
  273. t.Fatalf("TestContext(): got %v, want no error", err)
  274. }
  275. defer remove()
  276. res := proxyutil.NewResponse(http.StatusOK, nil, req)
  277. if err := reqmod.ModifyRequest(req); err != nil {
  278. t.Fatalf("ModifyRequest(): got %v, want no error", err)
  279. }
  280. if err := resmod.ModifyResponse(res); err != nil {
  281. t.Fatalf("ModifyResponse(): got %v, want no error", err)
  282. }
  283. if got, want := res.Header.Get("Content-Type"), "text/plain; charset=utf-8"; got != want {
  284. t.Errorf("res.Header.Get('Content-Type'): got %v, want %v", got, want)
  285. }
  286. got, err := ioutil.ReadAll(res.Body)
  287. if err != nil {
  288. t.Fatalf("ioutil.ReadAll(): got %v, want no error", err)
  289. }
  290. res.Body.Close()
  291. if want := []byte("test file"); !bytes.Equal(got, want) {
  292. t.Errorf("res.Body: got %q, want %q", got, want)
  293. }
  294. if got, want := res.ContentLength, int64(len("test file")); got != want {
  295. t.Errorf("res.ContentLength: got %v, want %v", got, want)
  296. }
  297. req, err = http.NewRequest("GET", "/foo/bar.baz", nil)
  298. if err != nil {
  299. t.Fatalf("NewRequest(): got %v, want no error", err)
  300. }
  301. _, remove, err = martian.TestContext(req, nil, nil)
  302. if err != nil {
  303. t.Fatalf("TestContext(): got %v, want no error", err)
  304. }
  305. defer remove()
  306. res = proxyutil.NewResponse(http.StatusOK, nil, req)
  307. if err := reqmod.ModifyRequest(req); err != nil {
  308. t.Fatalf("ModifyRequest(): got %v, want no error", err)
  309. }
  310. if err := resmod.ModifyResponse(res); err != nil {
  311. t.Fatalf("ModifyResponse(): got %v, want no error", err)
  312. }
  313. if got, want := res.Header.Get("Content-Type"), "text/plain; charset=utf-8"; got != want {
  314. t.Errorf("res.Header.Get('Content-Type'): got %v, want %v", got, want)
  315. }
  316. got, err = ioutil.ReadAll(res.Body)
  317. if err != nil {
  318. t.Fatalf("ioutil.ReadAll(): got %v, want no error", err)
  319. }
  320. res.Body.Close()
  321. if want := []byte("test file2"); !bytes.Equal(got, want) {
  322. t.Errorf("res.Body: got %q, want %q", got, want)
  323. }
  324. if got, want := res.ContentLength, int64(len("test file2")); got != want {
  325. t.Errorf("res.ContentLength: got %v, want %v", got, want)
  326. }
  327. }
  328. func TestStaticModifierSingleRangeRequest(t *testing.T) {
  329. tmpdir, err := ioutil.TempDir("", "test_static_modifier_on_request_")
  330. if err != nil {
  331. t.Fatalf("ioutil.TempDir(): got %v, want no error", err)
  332. }
  333. mod := NewModifier(tmpdir)
  334. if err := ioutil.WriteFile(path.Join(tmpdir, "sfmtest.txt"), []byte("0123456789"), 0777); err != nil {
  335. t.Fatalf("ioutil.WriteFile(): got %v, want no error", err)
  336. }
  337. req, err := http.NewRequest("GET", "/sfmtest.txt", nil)
  338. if err != nil {
  339. t.Fatalf("NewRequest(): got %v, want no error", err)
  340. }
  341. req.Header.Set("Range", "bytes=1-4")
  342. _, remove, err := martian.TestContext(req, nil, nil)
  343. if err != nil {
  344. t.Fatalf("TestContext(): got %v, want no error", err)
  345. }
  346. defer remove()
  347. if err := mod.ModifyRequest(req); err != nil {
  348. t.Fatalf("ModifyRequest(): got %v, want no error", err)
  349. }
  350. res := proxyutil.NewResponse(http.StatusOK, nil, req)
  351. if err := mod.ModifyResponse(res); err != nil {
  352. t.Fatalf("ModifyResponse(): got %v, want no error", err)
  353. }
  354. if got, want := res.StatusCode, http.StatusPartialContent; got != want {
  355. t.Errorf("res.Status: got %v, want %v", got, want)
  356. }
  357. if got, want := res.ContentLength, int64(len([]byte("1234"))); got != want {
  358. t.Errorf("res.ContentLength: got %d, want %d", got, want)
  359. }
  360. if got, want := res.Header.Get("Content-Range"), "bytes 1-4/10"; got != want {
  361. t.Errorf("res.Header.Get(%q): got %q, want %q", "Content-Encoding", got, want)
  362. }
  363. got, err := ioutil.ReadAll(res.Body)
  364. if err != nil {
  365. t.Fatalf("ioutil.ReadAll(): got %v, want no error", err)
  366. }
  367. res.Body.Close()
  368. if want := []byte("1234"); !bytes.Equal(got, want) {
  369. t.Errorf("res.Body: got %q, want %q", got, want)
  370. }
  371. if got, want := res.Header.Get("Content-Type"), "text/plain; charset=utf-8"; got != want {
  372. t.Errorf("res.Header.Get('Content-Type'): got %v, want %v", got, want)
  373. }
  374. }