rfc7231_SUITE.erl 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  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. %% @todo Should probably disable CONNECT and TRACE entirely until they're implemented.
  116. %method_connect(Config) ->
  117. method_options(Config) ->
  118. doc("The OPTIONS method is accepted. (RFC7231 4.3.7)"),
  119. ConnPid = gun_open(Config),
  120. Ref = gun:options(ConnPid, "/echo/method", [
  121. {<<"accept-encoding">>, <<"gzip">>}
  122. ]),
  123. {response, nofin, 200, _} = gun:await(ConnPid, Ref),
  124. {ok, <<"OPTIONS">>} = gun:await_body(ConnPid, Ref),
  125. ok.
  126. %method_options_asterisk(Config) ->
  127. %method_options_content_length_0(Config) ->
  128. %method_trace(Config) ->
  129. %% Request headers.
  130. %% @todo
  131. %% Status codes.
  132. status_code_100(Config) ->
  133. doc("The 100 Continue status code can be sent. (RFC7231 6.2.1)"),
  134. ConnPid = gun_open(Config),
  135. Ref = gun:get(ConnPid, "/resp/inform2/100", [
  136. {<<"accept-encoding">>, <<"gzip">>}
  137. ]),
  138. {inform, 100, []} = gun:await(ConnPid, Ref),
  139. ok.
  140. %http10_status_code_100(Config) ->
  141. status_code_101(Config) ->
  142. doc("The 101 Switching Protocols status code can be sent. (RFC7231 6.2.2)"),
  143. ConnPid = gun_open(Config),
  144. Ref = gun:get(ConnPid, "/resp/inform2/101", [
  145. {<<"accept-encoding">>, <<"gzip">>}
  146. ]),
  147. {inform, 101, []} = gun:await(ConnPid, Ref),
  148. ok.
  149. %http10_status_code_100(Config) ->
  150. status_code_200(Config) ->
  151. doc("The 200 OK status code can be sent. (RFC7231 6.3.1)"),
  152. ConnPid = gun_open(Config),
  153. Ref = gun:get(ConnPid, "/resp/reply2/200", [
  154. {<<"accept-encoding">>, <<"gzip">>}
  155. ]),
  156. {response, _, 200, _} = gun:await(ConnPid, Ref),
  157. ok.
  158. status_code_201(Config) ->
  159. doc("The 201 Created status code can be sent. (RFC7231 6.3.2)"),
  160. ConnPid = gun_open(Config),
  161. Ref = gun:get(ConnPid, "/resp/reply2/201", [
  162. {<<"accept-encoding">>, <<"gzip">>}
  163. ]),
  164. {response, _, 201, _} = gun:await(ConnPid, Ref),
  165. ok.
  166. status_code_202(Config) ->
  167. doc("The 202 Accepted status code can be sent. (RFC7231 6.3.3)"),
  168. ConnPid = gun_open(Config),
  169. Ref = gun:get(ConnPid, "/resp/reply2/202", [
  170. {<<"accept-encoding">>, <<"gzip">>}
  171. ]),
  172. {response, _, 202, _} = gun:await(ConnPid, Ref),
  173. ok.
  174. status_code_203(Config) ->
  175. doc("The 203 Non-Authoritative Information status code can be sent. (RFC7231 6.3.4)"),
  176. ConnPid = gun_open(Config),
  177. Ref = gun:get(ConnPid, "/resp/reply2/203", [
  178. {<<"accept-encoding">>, <<"gzip">>}
  179. ]),
  180. {response, _, 203, _} = gun:await(ConnPid, Ref),
  181. ok.
  182. status_code_204(Config) ->
  183. doc("The 204 No Content status code can be sent. (RFC7231 6.3.5)"),
  184. ConnPid = gun_open(Config),
  185. Ref = gun:get(ConnPid, "/resp/reply2/204", [
  186. {<<"accept-encoding">>, <<"gzip">>}
  187. ]),
  188. {response, _, 204, _} = gun:await(ConnPid, Ref),
  189. ok.
  190. status_code_205(Config) ->
  191. doc("The 205 Reset Content status code can be sent. (RFC7231 6.3.6)"),
  192. ConnPid = gun_open(Config),
  193. Ref = gun:get(ConnPid, "/resp/reply2/205", [
  194. {<<"accept-encoding">>, <<"gzip">>}
  195. ]),
  196. {response, _, 205, _} = gun:await(ConnPid, Ref),
  197. ok.
  198. status_code_300(Config) ->
  199. doc("The 300 Multiple Choices status code can be sent. (RFC7231 6.4.1)"),
  200. ConnPid = gun_open(Config),
  201. Ref = gun:get(ConnPid, "/resp/reply2/300", [
  202. {<<"accept-encoding">>, <<"gzip">>}
  203. ]),
  204. {response, _, 300, _} = gun:await(ConnPid, Ref),
  205. ok.
  206. status_code_301(Config) ->
  207. doc("The 301 Moved Permanently status code can be sent. (RFC7231 6.4.2)"),
  208. ConnPid = gun_open(Config),
  209. Ref = gun:get(ConnPid, "/resp/reply2/301", [
  210. {<<"accept-encoding">>, <<"gzip">>}
  211. ]),
  212. {response, _, 301, _} = gun:await(ConnPid, Ref),
  213. ok.
  214. status_code_302(Config) ->
  215. doc("The 302 Found status code can be sent. (RFC7231 6.4.3)"),
  216. ConnPid = gun_open(Config),
  217. Ref = gun:get(ConnPid, "/resp/reply2/302", [
  218. {<<"accept-encoding">>, <<"gzip">>}
  219. ]),
  220. {response, _, 302, _} = gun:await(ConnPid, Ref),
  221. ok.
  222. status_code_303(Config) ->
  223. doc("The 303 See Other status code can be sent. (RFC7231 6.4.4)"),
  224. ConnPid = gun_open(Config),
  225. Ref = gun:get(ConnPid, "/resp/reply2/303", [
  226. {<<"accept-encoding">>, <<"gzip">>}
  227. ]),
  228. {response, _, 303, _} = gun:await(ConnPid, Ref),
  229. ok.
  230. status_code_305(Config) ->
  231. doc("The 305 Use Proxy status code can be sent. (RFC7231 6.4.5)"),
  232. ConnPid = gun_open(Config),
  233. Ref = gun:get(ConnPid, "/resp/reply2/305", [
  234. {<<"accept-encoding">>, <<"gzip">>}
  235. ]),
  236. {response, _, 305, _} = gun:await(ConnPid, Ref),
  237. ok.
  238. %% The status code 306 is no longer used. (RFC7231 6.4.6)
  239. status_code_307(Config) ->
  240. doc("The 307 Temporary Redirect status code can be sent. (RFC7231 6.4.7)"),
  241. ConnPid = gun_open(Config),
  242. Ref = gun:get(ConnPid, "/resp/reply2/307", [
  243. {<<"accept-encoding">>, <<"gzip">>}
  244. ]),
  245. {response, _, 307, _} = gun:await(ConnPid, Ref),
  246. ok.
  247. status_code_400(Config) ->
  248. doc("The 400 Bad Request status code can be sent. (RFC7231 6.5.1)"),
  249. ConnPid = gun_open(Config),
  250. Ref = gun:get(ConnPid, "/resp/reply2/400", [
  251. {<<"accept-encoding">>, <<"gzip">>}
  252. ]),
  253. {response, _, 400, _} = gun:await(ConnPid, Ref),
  254. ok.
  255. status_code_402(Config) ->
  256. doc("The 402 Payment Required status code can be sent. (RFC7231 6.5.2)"),
  257. ConnPid = gun_open(Config),
  258. Ref = gun:get(ConnPid, "/resp/reply2/402", [
  259. {<<"accept-encoding">>, <<"gzip">>}
  260. ]),
  261. {response, _, 402, _} = gun:await(ConnPid, Ref),
  262. ok.
  263. status_code_403(Config) ->
  264. doc("The 403 Forbidden status code can be sent. (RFC7231 6.5.3)"),
  265. ConnPid = gun_open(Config),
  266. Ref = gun:get(ConnPid, "/resp/reply2/403", [
  267. {<<"accept-encoding">>, <<"gzip">>}
  268. ]),
  269. {response, _, 403, _} = gun:await(ConnPid, Ref),
  270. ok.
  271. status_code_404(Config) ->
  272. doc("The 404 Not Found status code can be sent. (RFC7231 6.5.4)"),
  273. ConnPid = gun_open(Config),
  274. Ref = gun:get(ConnPid, "/resp/reply2/404", [
  275. {<<"accept-encoding">>, <<"gzip">>}
  276. ]),
  277. {response, _, 404, _} = gun:await(ConnPid, Ref),
  278. ok.
  279. status_code_405(Config) ->
  280. doc("The 405 Method Not Allowed status code can be sent. (RFC7231 6.5.5)"),
  281. ConnPid = gun_open(Config),
  282. Ref = gun:get(ConnPid, "/resp/reply2/405", [
  283. {<<"accept-encoding">>, <<"gzip">>}
  284. ]),
  285. {response, _, 405, _} = gun:await(ConnPid, Ref),
  286. ok.
  287. status_code_406(Config) ->
  288. doc("The 406 Not Acceptable status code can be sent. (RFC7231 6.5.6)"),
  289. ConnPid = gun_open(Config),
  290. Ref = gun:get(ConnPid, "/resp/reply2/406", [
  291. {<<"accept-encoding">>, <<"gzip">>}
  292. ]),
  293. {response, _, 406, _} = gun:await(ConnPid, Ref),
  294. ok.
  295. status_code_408(Config) ->
  296. doc("The 408 Request Timeout status code can be sent. (RFC7231 6.5.7)"),
  297. ConnPid = gun_open(Config),
  298. Ref = gun:get(ConnPid, "/resp/reply2/408", [
  299. {<<"accept-encoding">>, <<"gzip">>}
  300. ]),
  301. {response, _, 408, _} = gun:await(ConnPid, Ref),
  302. ok.
  303. status_code_409(Config) ->
  304. doc("The 409 Conflict status code can be sent. (RFC7231 6.5.8)"),
  305. ConnPid = gun_open(Config),
  306. Ref = gun:get(ConnPid, "/resp/reply2/409", [
  307. {<<"accept-encoding">>, <<"gzip">>}
  308. ]),
  309. {response, _, 409, _} = gun:await(ConnPid, Ref),
  310. ok.
  311. status_code_410(Config) ->
  312. doc("The 410 Gone status code can be sent. (RFC7231 6.5.9)"),
  313. ConnPid = gun_open(Config),
  314. Ref = gun:get(ConnPid, "/resp/reply2/410", [
  315. {<<"accept-encoding">>, <<"gzip">>}
  316. ]),
  317. {response, _, 410, _} = gun:await(ConnPid, Ref),
  318. ok.
  319. status_code_411(Config) ->
  320. doc("The 411 Length Required status code can be sent. (RFC7231 6.5.10)"),
  321. ConnPid = gun_open(Config),
  322. Ref = gun:get(ConnPid, "/resp/reply2/411", [
  323. {<<"accept-encoding">>, <<"gzip">>}
  324. ]),
  325. {response, _, 411, _} = gun:await(ConnPid, Ref),
  326. ok.
  327. status_code_413(Config) ->
  328. doc("The 413 Payload Too Large status code can be sent. (RFC7231 6.5.11)"),
  329. ConnPid = gun_open(Config),
  330. Ref = gun:get(ConnPid, "/resp/reply2/413", [
  331. {<<"accept-encoding">>, <<"gzip">>}
  332. ]),
  333. {response, _, 413, _} = gun:await(ConnPid, Ref),
  334. ok.
  335. status_code_414(Config) ->
  336. doc("The 414 URI Too Long status code can be sent. (RFC7231 6.5.12)"),
  337. ConnPid = gun_open(Config),
  338. Ref = gun:get(ConnPid, "/resp/reply2/414", [
  339. {<<"accept-encoding">>, <<"gzip">>}
  340. ]),
  341. {response, _, 414, _} = gun:await(ConnPid, Ref),
  342. ok.
  343. status_code_415(Config) ->
  344. doc("The 415 Unsupported Media Type status code can be sent. (RFC7231 6.5.13)"),
  345. ConnPid = gun_open(Config),
  346. Ref = gun:get(ConnPid, "/resp/reply2/415", [
  347. {<<"accept-encoding">>, <<"gzip">>}
  348. ]),
  349. {response, _, 415, _} = gun:await(ConnPid, Ref),
  350. ok.
  351. status_code_417(Config) ->
  352. doc("The 417 Expectation Failed status code can be sent. (RFC7231 6.5.14)"),
  353. ConnPid = gun_open(Config),
  354. Ref = gun:get(ConnPid, "/resp/reply2/417", [
  355. {<<"accept-encoding">>, <<"gzip">>}
  356. ]),
  357. {response, _, 417, _} = gun:await(ConnPid, Ref),
  358. ok.
  359. status_code_426(Config) ->
  360. doc("The 426 Upgrade Required status code can be sent. (RFC7231 6.5.15)"),
  361. ConnPid = gun_open(Config),
  362. Ref = gun:get(ConnPid, "/resp/reply2/426", [
  363. {<<"accept-encoding">>, <<"gzip">>}
  364. ]),
  365. {response, _, 426, _} = gun:await(ConnPid, Ref),
  366. ok.
  367. status_code_500(Config) ->
  368. doc("The 500 Internal Server Error status code can be sent. (RFC7231 6.6.1)"),
  369. ConnPid = gun_open(Config),
  370. Ref = gun:get(ConnPid, "/resp/reply2/500", [
  371. {<<"accept-encoding">>, <<"gzip">>}
  372. ]),
  373. {response, _, 500, _} = gun:await(ConnPid, Ref),
  374. ok.
  375. status_code_501(Config) ->
  376. doc("The 501 Not Implemented status code can be sent. (RFC7231 6.6.2)"),
  377. ConnPid = gun_open(Config),
  378. Ref = gun:get(ConnPid, "/resp/reply2/501", [
  379. {<<"accept-encoding">>, <<"gzip">>}
  380. ]),
  381. {response, _, 501, _} = gun:await(ConnPid, Ref),
  382. ok.
  383. status_code_502(Config) ->
  384. doc("The 502 Bad Gateway status code can be sent. (RFC7231 6.6.3)"),
  385. ConnPid = gun_open(Config),
  386. Ref = gun:get(ConnPid, "/resp/reply2/502", [
  387. {<<"accept-encoding">>, <<"gzip">>}
  388. ]),
  389. {response, _, 502, _} = gun:await(ConnPid, Ref),
  390. ok.
  391. status_code_503(Config) ->
  392. doc("The 503 Service Unavailable status code can be sent. (RFC7231 6.6.4)"),
  393. ConnPid = gun_open(Config),
  394. Ref = gun:get(ConnPid, "/resp/reply2/503", [
  395. {<<"accept-encoding">>, <<"gzip">>}
  396. ]),
  397. {response, _, 503, _} = gun:await(ConnPid, Ref),
  398. ok.
  399. status_code_504(Config) ->
  400. doc("The 504 Gateway Timeout status code can be sent. (RFC7231 6.6.5)"),
  401. ConnPid = gun_open(Config),
  402. Ref = gun:get(ConnPid, "/resp/reply2/504", [
  403. {<<"accept-encoding">>, <<"gzip">>}
  404. ]),
  405. {response, _, 504, _} = gun:await(ConnPid, Ref),
  406. ok.
  407. status_code_505(Config) ->
  408. doc("The 505 HTTP Version Not Supported status code can be sent. (RFC7231 6.6.6)"),
  409. ConnPid = gun_open(Config),
  410. Ref = gun:get(ConnPid, "/resp/reply2/505", [
  411. {<<"accept-encoding">>, <<"gzip">>}
  412. ]),
  413. {response, _, 505, _} = gun:await(ConnPid, Ref),
  414. ok.