rfc7231_SUITE.erl 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. %% Copyright (c) 2017, Loïc Hoguin <essen@ninenines.eu>
  2. %%
  3. %% Permission to use, copy, modify, and/or distribute this software for any
  4. %% purpose with or without fee is hereby granted, provided that the above
  5. %% copyright notice and this permission notice appear in all copies.
  6. %%
  7. %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  8. %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  9. %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  10. %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  11. %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  12. %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  13. %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  14. -module(rfc7231_SUITE).
  15. -compile(export_all).
  16. -compile(nowarn_export_all).
  17. -import(ct_helper, [config/2]).
  18. -import(ct_helper, [doc/1]).
  19. -import(cowboy_test, [gun_open/1]).
  20. all() ->
  21. cowboy_test:common_all().
  22. groups() ->
  23. cowboy_test:common_groups(ct_helper:all(?MODULE)).
  24. init_per_group(Name, Config) ->
  25. cowboy_test:init_common_groups(Name, Config, ?MODULE).
  26. end_per_group(Name, _) ->
  27. cowboy:stop_listener(Name).
  28. init_dispatch(_) ->
  29. cowboy_router:compile([{"[...]", [
  30. {"/", hello_h, []},
  31. {"/echo/:key", echo_h, []},
  32. {"/resp/:key[/:arg]", resp_h, []}
  33. ]}]).
  34. %% @todo The documentation should list what methods, headers and status codes
  35. %% are handled automatically so users can know what befalls to them to implement.
  36. %% Methods.
  37. method_get(Config) ->
  38. doc("The GET method is accepted. (RFC7231 4.3.1)"),
  39. ConnPid = gun_open(Config),
  40. Ref = gun:get(ConnPid, "/", [
  41. {<<"accept-encoding">>, <<"gzip">>}
  42. ]),
  43. {response, nofin, 200, _} = gun:await(ConnPid, Ref),
  44. {ok, <<"Hello world!">>} = gun:await_body(ConnPid, Ref),
  45. ok.
  46. method_head(Config) ->
  47. doc("The HEAD method is accepted. (RFC7231 4.3.2)"),
  48. ConnPid = gun_open(Config),
  49. Ref = gun:head(ConnPid, "/", [
  50. {<<"accept-encoding">>, <<"gzip">>}
  51. ]),
  52. {response, fin, 200, _} = gun:await(ConnPid, Ref),
  53. ok.
  54. method_head_same_resp_headers_as_get(Config) ->
  55. doc("Responses to HEAD should return the same headers as GET. (RFC7231 4.3.2)"),
  56. ConnPid = gun_open(Config),
  57. Ref1 = gun:get(ConnPid, "/", [
  58. {<<"accept-encoding">>, <<"gzip">>}
  59. ]),
  60. {response, nofin, 200, Headers1} = gun:await(ConnPid, Ref1),
  61. {ok, <<"Hello world!">>} = gun:await_body(ConnPid, Ref1),
  62. Ref2 = gun:head(ConnPid, "/", [
  63. {<<"accept-encoding">>, <<"gzip">>}
  64. ]),
  65. {response, fin, 200, Headers2} = gun:await(ConnPid, Ref2),
  66. %% We remove the date header since the date might have changed between requests.
  67. Headers = lists:keydelete(<<"date">>, 1, Headers1),
  68. Headers = lists:keydelete(<<"date">>, 1, Headers2),
  69. ok.
  70. method_head_same_resp_headers_as_get_stream_reply(Config) ->
  71. doc("Responses to HEAD should return the same headers as GET. (RFC7231 4.3.2)"),
  72. ConnPid = gun_open(Config),
  73. Ref1 = gun:get(ConnPid, "/resp/stream_reply2/200", [
  74. {<<"accept-encoding">>, <<"gzip">>}
  75. ]),
  76. {response, nofin, 200, Headers1} = gun:await(ConnPid, Ref1),
  77. {ok, _} = gun:await_body(ConnPid, Ref1),
  78. Ref2 = gun:head(ConnPid, "/resp/stream_reply2/200", [
  79. {<<"accept-encoding">>, <<"gzip">>}
  80. ]),
  81. {response, fin, 200, Headers2} = gun:await(ConnPid, Ref2),
  82. %% We remove the date header since the date might have changed between requests.
  83. Headers = lists:keydelete(<<"date">>, 1, Headers1),
  84. Headers = lists:keydelete(<<"date">>, 1, Headers2),
  85. ok.
  86. method_post(Config) ->
  87. doc("The POST method is accepted. (RFC7231 4.3.3)"),
  88. ConnPid = gun_open(Config),
  89. Ref = gun:post(ConnPid, "/echo/read_body", [
  90. {<<"accept-encoding">>, <<"gzip">>},
  91. {<<"content-type">>, <<"application/x-www-form-urlencoded">>}
  92. ], <<"hello=world">>),
  93. {response, nofin, 200, _} = gun:await(ConnPid, Ref),
  94. {ok, <<"hello=world">>} = gun:await_body(ConnPid, Ref),
  95. ok.
  96. method_put(Config) ->
  97. doc("The PUT method is accepted. (RFC7231 4.3.4)"),
  98. ConnPid = gun_open(Config),
  99. Ref = gun:put(ConnPid, "/echo/read_body", [
  100. {<<"accept-encoding">>, <<"gzip">>},
  101. {<<"content-type">>, <<"application/x-www-form-urlencoded">>}
  102. ], <<"hello=world">>),
  103. {response, nofin, 200, _} = gun:await(ConnPid, Ref),
  104. {ok, <<"hello=world">>} = gun:await_body(ConnPid, Ref),
  105. ok.
  106. method_delete(Config) ->
  107. doc("The DELETE method is accepted. (RFC7231 4.3.5)"),
  108. ConnPid = gun_open(Config),
  109. Ref = gun:delete(ConnPid, "/echo/method", [
  110. {<<"accept-encoding">>, <<"gzip">>}
  111. ]),
  112. {response, nofin, 200, _} = gun:await(ConnPid, Ref),
  113. {ok, <<"DELETE">>} = gun:await_body(ConnPid, Ref),
  114. ok.
  115. method_connect(Config) ->
  116. doc("The CONNECT method is currently not implemented. (RFC7231 4.3.6)"),
  117. ConnPid = gun_open(Config),
  118. Ref = gun:request(ConnPid, <<"CONNECT">>, "localhost:8080", [
  119. {<<"accept-encoding">>, <<"gzip">>}
  120. ]),
  121. {response, fin, 501, _} = gun:await(ConnPid, Ref),
  122. ok.
  123. method_options(Config) ->
  124. doc("The OPTIONS method is accepted. (RFC7231 4.3.7)"),
  125. ConnPid = gun_open(Config),
  126. Ref = gun:options(ConnPid, "/echo/method", [
  127. {<<"accept-encoding">>, <<"gzip">>}
  128. ]),
  129. {response, nofin, 200, _} = gun:await(ConnPid, Ref),
  130. {ok, <<"OPTIONS">>} = gun:await_body(ConnPid, Ref),
  131. ok.
  132. %method_options_asterisk(Config) ->
  133. %method_options_content_length_0(Config) ->
  134. method_trace(Config) ->
  135. doc("The TRACE method is currently not implemented. (RFC7231 4.3.8)"),
  136. ConnPid = gun_open(Config),
  137. Ref = gun:request(ConnPid, <<"TRACE">>, "/", [
  138. {<<"accept-encoding">>, <<"gzip">>}
  139. ]),
  140. {response, fin, 501, _} = gun:await(ConnPid, Ref),
  141. ok.
  142. %% Request headers.
  143. %% @todo
  144. %% Status codes.
  145. status_code_100(Config) ->
  146. doc("The 100 Continue status code can be sent. (RFC7231 6.2.1)"),
  147. ConnPid = gun_open(Config),
  148. Ref = gun:get(ConnPid, "/resp/inform2/100", [
  149. {<<"accept-encoding">>, <<"gzip">>}
  150. ]),
  151. {inform, 100, []} = gun:await(ConnPid, Ref),
  152. ok.
  153. %http10_status_code_100(Config) ->
  154. status_code_101(Config) ->
  155. doc("The 101 Switching Protocols status code can be sent. (RFC7231 6.2.2)"),
  156. ConnPid = gun_open(Config),
  157. Ref = gun:get(ConnPid, "/resp/inform2/101", [
  158. {<<"accept-encoding">>, <<"gzip">>}
  159. ]),
  160. {inform, 101, []} = gun:await(ConnPid, Ref),
  161. ok.
  162. %http10_status_code_100(Config) ->
  163. status_code_200(Config) ->
  164. doc("The 200 OK status code can be sent. (RFC7231 6.3.1)"),
  165. ConnPid = gun_open(Config),
  166. Ref = gun:get(ConnPid, "/resp/reply2/200", [
  167. {<<"accept-encoding">>, <<"gzip">>}
  168. ]),
  169. {response, _, 200, _} = gun:await(ConnPid, Ref),
  170. ok.
  171. status_code_201(Config) ->
  172. doc("The 201 Created status code can be sent. (RFC7231 6.3.2)"),
  173. ConnPid = gun_open(Config),
  174. Ref = gun:get(ConnPid, "/resp/reply2/201", [
  175. {<<"accept-encoding">>, <<"gzip">>}
  176. ]),
  177. {response, _, 201, _} = gun:await(ConnPid, Ref),
  178. ok.
  179. status_code_202(Config) ->
  180. doc("The 202 Accepted status code can be sent. (RFC7231 6.3.3)"),
  181. ConnPid = gun_open(Config),
  182. Ref = gun:get(ConnPid, "/resp/reply2/202", [
  183. {<<"accept-encoding">>, <<"gzip">>}
  184. ]),
  185. {response, _, 202, _} = gun:await(ConnPid, Ref),
  186. ok.
  187. status_code_203(Config) ->
  188. doc("The 203 Non-Authoritative Information status code can be sent. (RFC7231 6.3.4)"),
  189. ConnPid = gun_open(Config),
  190. Ref = gun:get(ConnPid, "/resp/reply2/203", [
  191. {<<"accept-encoding">>, <<"gzip">>}
  192. ]),
  193. {response, _, 203, _} = gun:await(ConnPid, Ref),
  194. ok.
  195. status_code_204(Config) ->
  196. doc("The 204 No Content status code can be sent. (RFC7231 6.3.5)"),
  197. ConnPid = gun_open(Config),
  198. Ref = gun:get(ConnPid, "/resp/reply2/204", [
  199. {<<"accept-encoding">>, <<"gzip">>}
  200. ]),
  201. {response, _, 204, _} = gun:await(ConnPid, Ref),
  202. ok.
  203. status_code_205(Config) ->
  204. doc("The 205 Reset Content status code can be sent. (RFC7231 6.3.6)"),
  205. ConnPid = gun_open(Config),
  206. Ref = gun:get(ConnPid, "/resp/reply2/205", [
  207. {<<"accept-encoding">>, <<"gzip">>}
  208. ]),
  209. {response, _, 205, _} = gun:await(ConnPid, Ref),
  210. ok.
  211. status_code_300(Config) ->
  212. doc("The 300 Multiple Choices status code can be sent. (RFC7231 6.4.1)"),
  213. ConnPid = gun_open(Config),
  214. Ref = gun:get(ConnPid, "/resp/reply2/300", [
  215. {<<"accept-encoding">>, <<"gzip">>}
  216. ]),
  217. {response, _, 300, _} = gun:await(ConnPid, Ref),
  218. ok.
  219. status_code_301(Config) ->
  220. doc("The 301 Moved Permanently status code can be sent. (RFC7231 6.4.2)"),
  221. ConnPid = gun_open(Config),
  222. Ref = gun:get(ConnPid, "/resp/reply2/301", [
  223. {<<"accept-encoding">>, <<"gzip">>}
  224. ]),
  225. {response, _, 301, _} = gun:await(ConnPid, Ref),
  226. ok.
  227. status_code_302(Config) ->
  228. doc("The 302 Found status code can be sent. (RFC7231 6.4.3)"),
  229. ConnPid = gun_open(Config),
  230. Ref = gun:get(ConnPid, "/resp/reply2/302", [
  231. {<<"accept-encoding">>, <<"gzip">>}
  232. ]),
  233. {response, _, 302, _} = gun:await(ConnPid, Ref),
  234. ok.
  235. status_code_303(Config) ->
  236. doc("The 303 See Other status code can be sent. (RFC7231 6.4.4)"),
  237. ConnPid = gun_open(Config),
  238. Ref = gun:get(ConnPid, "/resp/reply2/303", [
  239. {<<"accept-encoding">>, <<"gzip">>}
  240. ]),
  241. {response, _, 303, _} = gun:await(ConnPid, Ref),
  242. ok.
  243. status_code_305(Config) ->
  244. doc("The 305 Use Proxy status code can be sent. (RFC7231 6.4.5)"),
  245. ConnPid = gun_open(Config),
  246. Ref = gun:get(ConnPid, "/resp/reply2/305", [
  247. {<<"accept-encoding">>, <<"gzip">>}
  248. ]),
  249. {response, _, 305, _} = gun:await(ConnPid, Ref),
  250. ok.
  251. %% The status code 306 is no longer used. (RFC7231 6.4.6)
  252. status_code_307(Config) ->
  253. doc("The 307 Temporary Redirect status code can be sent. (RFC7231 6.4.7)"),
  254. ConnPid = gun_open(Config),
  255. Ref = gun:get(ConnPid, "/resp/reply2/307", [
  256. {<<"accept-encoding">>, <<"gzip">>}
  257. ]),
  258. {response, _, 307, _} = gun:await(ConnPid, Ref),
  259. ok.
  260. status_code_400(Config) ->
  261. doc("The 400 Bad Request status code can be sent. (RFC7231 6.5.1)"),
  262. ConnPid = gun_open(Config),
  263. Ref = gun:get(ConnPid, "/resp/reply2/400", [
  264. {<<"accept-encoding">>, <<"gzip">>}
  265. ]),
  266. {response, _, 400, _} = gun:await(ConnPid, Ref),
  267. ok.
  268. status_code_402(Config) ->
  269. doc("The 402 Payment Required status code can be sent. (RFC7231 6.5.2)"),
  270. ConnPid = gun_open(Config),
  271. Ref = gun:get(ConnPid, "/resp/reply2/402", [
  272. {<<"accept-encoding">>, <<"gzip">>}
  273. ]),
  274. {response, _, 402, _} = gun:await(ConnPid, Ref),
  275. ok.
  276. status_code_403(Config) ->
  277. doc("The 403 Forbidden status code can be sent. (RFC7231 6.5.3)"),
  278. ConnPid = gun_open(Config),
  279. Ref = gun:get(ConnPid, "/resp/reply2/403", [
  280. {<<"accept-encoding">>, <<"gzip">>}
  281. ]),
  282. {response, _, 403, _} = gun:await(ConnPid, Ref),
  283. ok.
  284. status_code_404(Config) ->
  285. doc("The 404 Not Found status code can be sent. (RFC7231 6.5.4)"),
  286. ConnPid = gun_open(Config),
  287. Ref = gun:get(ConnPid, "/resp/reply2/404", [
  288. {<<"accept-encoding">>, <<"gzip">>}
  289. ]),
  290. {response, _, 404, _} = gun:await(ConnPid, Ref),
  291. ok.
  292. status_code_405(Config) ->
  293. doc("The 405 Method Not Allowed status code can be sent. (RFC7231 6.5.5)"),
  294. ConnPid = gun_open(Config),
  295. Ref = gun:get(ConnPid, "/resp/reply2/405", [
  296. {<<"accept-encoding">>, <<"gzip">>}
  297. ]),
  298. {response, _, 405, _} = gun:await(ConnPid, Ref),
  299. ok.
  300. status_code_406(Config) ->
  301. doc("The 406 Not Acceptable status code can be sent. (RFC7231 6.5.6)"),
  302. ConnPid = gun_open(Config),
  303. Ref = gun:get(ConnPid, "/resp/reply2/406", [
  304. {<<"accept-encoding">>, <<"gzip">>}
  305. ]),
  306. {response, _, 406, _} = gun:await(ConnPid, Ref),
  307. ok.
  308. status_code_408(Config) ->
  309. doc("The 408 Request Timeout status code can be sent. (RFC7231 6.5.7)"),
  310. ConnPid = gun_open(Config),
  311. Ref = gun:get(ConnPid, "/resp/reply2/408", [
  312. {<<"accept-encoding">>, <<"gzip">>}
  313. ]),
  314. {response, _, 408, _} = gun:await(ConnPid, Ref),
  315. ok.
  316. status_code_409(Config) ->
  317. doc("The 409 Conflict status code can be sent. (RFC7231 6.5.8)"),
  318. ConnPid = gun_open(Config),
  319. Ref = gun:get(ConnPid, "/resp/reply2/409", [
  320. {<<"accept-encoding">>, <<"gzip">>}
  321. ]),
  322. {response, _, 409, _} = gun:await(ConnPid, Ref),
  323. ok.
  324. status_code_410(Config) ->
  325. doc("The 410 Gone status code can be sent. (RFC7231 6.5.9)"),
  326. ConnPid = gun_open(Config),
  327. Ref = gun:get(ConnPid, "/resp/reply2/410", [
  328. {<<"accept-encoding">>, <<"gzip">>}
  329. ]),
  330. {response, _, 410, _} = gun:await(ConnPid, Ref),
  331. ok.
  332. status_code_411(Config) ->
  333. doc("The 411 Length Required status code can be sent. (RFC7231 6.5.10)"),
  334. ConnPid = gun_open(Config),
  335. Ref = gun:get(ConnPid, "/resp/reply2/411", [
  336. {<<"accept-encoding">>, <<"gzip">>}
  337. ]),
  338. {response, _, 411, _} = gun:await(ConnPid, Ref),
  339. ok.
  340. status_code_413(Config) ->
  341. doc("The 413 Payload Too Large status code can be sent. (RFC7231 6.5.11)"),
  342. ConnPid = gun_open(Config),
  343. Ref = gun:get(ConnPid, "/resp/reply2/413", [
  344. {<<"accept-encoding">>, <<"gzip">>}
  345. ]),
  346. {response, _, 413, _} = gun:await(ConnPid, Ref),
  347. ok.
  348. status_code_414(Config) ->
  349. doc("The 414 URI Too Long status code can be sent. (RFC7231 6.5.12)"),
  350. ConnPid = gun_open(Config),
  351. Ref = gun:get(ConnPid, "/resp/reply2/414", [
  352. {<<"accept-encoding">>, <<"gzip">>}
  353. ]),
  354. {response, _, 414, _} = gun:await(ConnPid, Ref),
  355. ok.
  356. status_code_415(Config) ->
  357. doc("The 415 Unsupported Media Type status code can be sent. (RFC7231 6.5.13)"),
  358. ConnPid = gun_open(Config),
  359. Ref = gun:get(ConnPid, "/resp/reply2/415", [
  360. {<<"accept-encoding">>, <<"gzip">>}
  361. ]),
  362. {response, _, 415, _} = gun:await(ConnPid, Ref),
  363. ok.
  364. status_code_417(Config) ->
  365. doc("The 417 Expectation Failed status code can be sent. (RFC7231 6.5.14)"),
  366. ConnPid = gun_open(Config),
  367. Ref = gun:get(ConnPid, "/resp/reply2/417", [
  368. {<<"accept-encoding">>, <<"gzip">>}
  369. ]),
  370. {response, _, 417, _} = gun:await(ConnPid, Ref),
  371. ok.
  372. status_code_426(Config) ->
  373. doc("The 426 Upgrade Required status code can be sent. (RFC7231 6.5.15)"),
  374. ConnPid = gun_open(Config),
  375. Ref = gun:get(ConnPid, "/resp/reply2/426", [
  376. {<<"accept-encoding">>, <<"gzip">>}
  377. ]),
  378. {response, _, 426, _} = gun:await(ConnPid, Ref),
  379. ok.
  380. status_code_500(Config) ->
  381. doc("The 500 Internal Server Error status code can be sent. (RFC7231 6.6.1)"),
  382. ConnPid = gun_open(Config),
  383. Ref = gun:get(ConnPid, "/resp/reply2/500", [
  384. {<<"accept-encoding">>, <<"gzip">>}
  385. ]),
  386. {response, _, 500, _} = gun:await(ConnPid, Ref),
  387. ok.
  388. status_code_501(Config) ->
  389. doc("The 501 Not Implemented status code can be sent. (RFC7231 6.6.2)"),
  390. ConnPid = gun_open(Config),
  391. Ref = gun:get(ConnPid, "/resp/reply2/501", [
  392. {<<"accept-encoding">>, <<"gzip">>}
  393. ]),
  394. {response, _, 501, _} = gun:await(ConnPid, Ref),
  395. ok.
  396. status_code_502(Config) ->
  397. doc("The 502 Bad Gateway status code can be sent. (RFC7231 6.6.3)"),
  398. ConnPid = gun_open(Config),
  399. Ref = gun:get(ConnPid, "/resp/reply2/502", [
  400. {<<"accept-encoding">>, <<"gzip">>}
  401. ]),
  402. {response, _, 502, _} = gun:await(ConnPid, Ref),
  403. ok.
  404. status_code_503(Config) ->
  405. doc("The 503 Service Unavailable status code can be sent. (RFC7231 6.6.4)"),
  406. ConnPid = gun_open(Config),
  407. Ref = gun:get(ConnPid, "/resp/reply2/503", [
  408. {<<"accept-encoding">>, <<"gzip">>}
  409. ]),
  410. {response, _, 503, _} = gun:await(ConnPid, Ref),
  411. ok.
  412. status_code_504(Config) ->
  413. doc("The 504 Gateway Timeout status code can be sent. (RFC7231 6.6.5)"),
  414. ConnPid = gun_open(Config),
  415. Ref = gun:get(ConnPid, "/resp/reply2/504", [
  416. {<<"accept-encoding">>, <<"gzip">>}
  417. ]),
  418. {response, _, 504, _} = gun:await(ConnPid, Ref),
  419. ok.
  420. status_code_505(Config) ->
  421. doc("The 505 HTTP Version Not Supported status code can be sent. (RFC7231 6.6.6)"),
  422. ConnPid = gun_open(Config),
  423. Ref = gun:get(ConnPid, "/resp/reply2/505", [
  424. {<<"accept-encoding">>, <<"gzip">>}
  425. ]),
  426. {response, _, 505, _} = gun:await(ConnPid, Ref),
  427. ok.