Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

520 Zeilen
18 KiB

vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
vor 3 Jahren
  1. -module(wsHttp).
  2. -include_lib("eNet/include/eNet.hrl").
  3. -include("wsCom.hrl").
  4. -export([
  5. start_link/1
  6. , sendResponse/5
  7. , sendFile/5
  8. %% Exported for looping with a fully-qualified module name
  9. , chunkLoop/1
  10. , spellHeaders/1
  11. , splitArgs/1
  12. , closeOrKeepAlive/2
  13. , maybeSendContinue/2
  14. ]).
  15. %% eNet callback
  16. -export([newCon/2]).
  17. -export([
  18. init_it/2
  19. , system_code_change/4
  20. , system_continue/3
  21. , system_get_state/1
  22. , system_terminate/4
  23. ]).
  24. newCon(_Sock, WsMod) ->
  25. ?MODULE:start_link(WsMod).
  26. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% genActor start %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  27. -spec(start_link(atom()) -> {ok, pid()} | ignore | {error, term()}).
  28. start_link(WsMod) ->
  29. proc_lib:start_link(?MODULE, init_it, [self(), WsMod], infinity, []).
  30. init_it(Parent, Args) ->
  31. process_flag(trap_exit, true),
  32. modInit(Parent, Args).
  33. -spec system_code_change(term(), module(), undefined | term(), term()) -> {ok, term()}.
  34. system_code_change(State, _Module, _OldVsn, _Extra) ->
  35. {ok, State}.
  36. -spec system_continue(pid(), [], {module(), atom(), pid(), term()}) -> ok.
  37. system_continue(_Parent, _Debug, {Parent, State}) ->
  38. loop(Parent, State).
  39. -spec system_get_state(term()) -> {ok, term()}.
  40. system_get_state(State) ->
  41. {ok, State}.
  42. -spec system_terminate(term(), pid(), [], term()) -> none().
  43. system_terminate(Reason, _Parent, _Debug, State) ->
  44. terminate(Reason, State).
  45. modInit(Parent, Args) ->
  46. case init(Args) of
  47. {ok, State} ->
  48. proc_lib:init_ack(Parent, {ok, self()}),
  49. loop(Parent, State);
  50. {stop, Reason} ->
  51. proc_lib:init_ack(Parent, {error, Reason}),
  52. exit(Reason)
  53. end.
  54. loop(Parent, State) ->
  55. receive
  56. {system, From, Request} ->
  57. sys:handle_system_msg(Request, From, Parent, ?MODULE, [], {Parent, State});
  58. {'EXIT', Parent, Reason} ->
  59. terminate(Reason, State);
  60. Msg ->
  61. case handleMsg(Msg, State) of
  62. kpS ->
  63. loop(Parent, State);
  64. {ok, NewState} ->
  65. loop(Parent, NewState);
  66. {stop, Reason} ->
  67. terminate(Reason, State),
  68. exit(Reason)
  69. end
  70. end.
  71. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% genActor end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  72. %% ************************************************ API ***************************************************************
  73. init(WsMod) ->
  74. {ok, #wsState{wsMod = WsMod}}.
  75. handleMsg({tcp, _Socket, Data}, State) ->
  76. #wsState{stage = Stage, buffer = Buffer, socket = Socket} = State,
  77. case wsHttpProtocol:request(Stage, <<Buffer/binary, Data/binary>>, State) of
  78. {ok, _NewState} = LRet ->
  79. LRet;
  80. {done, NewState} ->
  81. Response = doHandle(NewState),
  82. #wsState{buffer = Buffer, socket = Socket, temHeader = TemHeader, method = Method} = NewState,
  83. case doResponse(Response, Socket, TemHeader, Method) of
  84. keep_alive ->
  85. handleMsg({tcp, _Socket, Buffer}, newWsState(NewState));
  86. close ->
  87. {stop, close}
  88. end;
  89. Err ->
  90. ?wsErr("recv the http data error ~p~n", [Err]),
  91. sendBadRequest(Socket),
  92. {stop, close}
  93. end;
  94. handleMsg({tcp_closed, _Socket}, _State) ->
  95. {stop, tcp_closed};
  96. handleMsg({tcp_error, _Socket, Reason}, _State) ->
  97. ?wsErr("the http tcp socket error ~p~n", [Reason]),
  98. {stop, tcp_error};
  99. handleMsg({ssl, _Socket, Data}, State) ->
  100. #wsState{stage = Stage, buffer = Buffer, socket = Socket} = State,
  101. case wsHttpProtocol:request(Stage, <<Buffer/binary, Data/binary>>, State) of
  102. {ok, _NewState} = LRet ->
  103. LRet;
  104. {done, NewState} ->
  105. Response = doHandle(NewState),
  106. #wsState{buffer = Buffer, temHeader = TemHeader, method = Method} = NewState,
  107. case doResponse(Response, Socket, TemHeader, Method) of
  108. keep_alive ->
  109. handleMsg({tcp, Socket, Buffer}, newWsState(NewState));
  110. close ->
  111. {stop, close}
  112. end;
  113. Err ->
  114. ?wsErr("recv the http data error ~p~n", [Err]),
  115. sendBadRequest(Socket),
  116. {stop, close}
  117. end;
  118. handleMsg({ssl_closed, _Socket}, _State) ->
  119. {stop, ssl_closed};
  120. handleMsg({ssl_error, _Socket, Reason}, _State) ->
  121. ?wsErr("the http ssl socket error ~p~n", [Reason]),
  122. {stop, ssl_error};
  123. handleMsg({?mSockReady, Sock}, _State) ->
  124. inet:setopts(Sock, [{packet, raw}, {active, true}]),
  125. {ok, #wsState{socket = Sock}};
  126. handleMsg({?mSockReady, Sock, SslOpts, SslHSTet}, State) ->
  127. case wsSslAcceptor:handshake(Sock, SslOpts, SslHSTet) of
  128. {ok, SslSock} ->
  129. ssl:setopts(Sock, [{packet, raw}, {active, true}]),
  130. {ok, State#wsState{socket = SslSock, isSsl = true}};
  131. _Err ->
  132. ?wsErr("ssl handshake error ~p~n", [_Err]),
  133. {stop, handshake_error}
  134. end;
  135. handleMsg(_Msg, _State) ->
  136. ?wsErr("~p info receive unexpect msg ~p ~n ", [?MODULE, _Msg]),
  137. kpS.
  138. terminate(_Reason, #wsState{socket = Socket} = _State) ->
  139. wsNet:close(Socket),
  140. ok.
  141. newWsState(WsState) ->
  142. WsState#wsState{
  143. stage = reqLine
  144. , buffer = <<>>
  145. , wsReq = undefined
  146. , headerCnt = 0
  147. , temHeader = []
  148. , contentLength = undefined
  149. , temChunked = <<>>
  150. }.
  151. %% @doc Execute the user callback, translating failure into a proper response.
  152. doHandle(State) ->
  153. #wsState{wsMod = WsMod, method = Method, path = Path, wsReq = WsReq} = State,
  154. try WsMod:handle(Method, Path, WsReq) of
  155. %% {ok,...{file,...}}
  156. {ok, Headers, {file, Filename}} ->
  157. {file, 200, Headers, Filename, []};
  158. {ok, Headers, {file, Filename, Range}} ->
  159. {file, 200, Headers, Filename, Range};
  160. %% ok simple
  161. {ok, Headers, Body} -> {response, 200, Headers, Body};
  162. {ok, Body} -> {response, 200, [], Body};
  163. %% Chunk
  164. {chunk, Headers} -> {chunk, Headers, <<"">>};
  165. {chunk, Headers, Initial} -> {chunk, Headers, Initial};
  166. %% File
  167. {HttpCode, Headers, {file, Filename}} ->
  168. {file, HttpCode, Headers, Filename, {0, 0}};
  169. {HttpCode, Headers, {file, Filename, Range}} ->
  170. {file, HttpCode, Headers, Filename, Range};
  171. %% Simple
  172. {HttpCode, Headers, Body} -> {response, HttpCode, Headers, Body};
  173. {HttpCode, Body} -> {response, HttpCode, [], Body};
  174. %% Unexpected
  175. Unexpected ->
  176. ?wsErr("handle return error ~p ~p~n", [WsReq, Unexpected]),
  177. {response, 500, [], <<"Internal server error">>}
  178. catch
  179. throw:{ResponseCode, Headers, Body} when is_integer(ResponseCode) ->
  180. {response, ResponseCode, Headers, Body};
  181. throw:Exc:Stacktrace ->
  182. ?wsErr("handle catch throw ~p ~p ~p~n", [WsReq, Exc, Stacktrace]),
  183. {response, 500, [], <<"Internal server error">>};
  184. error:Error:Stacktrace ->
  185. ?wsErr("handle catch error ~p ~p ~p~n", [WsReq, Error, Stacktrace]),
  186. {response, 500, [], <<"Internal server error">>};
  187. exit:Exit:Stacktrace ->
  188. ?wsErr("handle catch exit ~p ~p ~p~n", [WsReq, Exit, Stacktrace]),
  189. {response, 500, [], <<"Internal server error">>}
  190. end.
  191. doResponse({response, Code, UserHeaders, Body}, Socket, TemHeader, Method) ->
  192. Headers = [connection(UserHeaders, TemHeader), contentLength(UserHeaders, Body) | UserHeaders],
  193. sendResponse(Socket, Method, Code, Headers, Body),
  194. closeOrKeepAlive(UserHeaders, TemHeader);
  195. doResponse({chunk, UserHeaders, Initial}, Socket, TemHeader, Method) ->
  196. ResponseHeaders = [transferEncoding(UserHeaders), connection(UserHeaders, TemHeader) | UserHeaders],
  197. sendResponse(Socket, Method, 200, ResponseHeaders, <<>>),
  198. Initial =:= <<"">> orelse sendChunk(Socket, Initial),
  199. case startChunkLoop(Socket) of
  200. {error, client_closed} -> client;
  201. ok -> server
  202. end,
  203. close;
  204. doResponse({file, ResponseCode, UserHeaders, Filename, Range}, Socket, TemHeader, Method) ->
  205. ResponseHeaders = [connection(UserHeaders, TemHeader) | UserHeaders],
  206. case wsUtil:fileSize(Filename) of
  207. {error, _FileError} ->
  208. sendSrvError(Socket),
  209. {stop, file_error};
  210. Size ->
  211. Ret =
  212. case wsUtil:normalizeRange(Range, Size) of
  213. undefined ->
  214. sendFile(Socket, ResponseCode, [{?CONTENT_LENGTH_HEADER, Size} | ResponseHeaders], Filename, {0, 0});
  215. {Offset, Length} ->
  216. ERange = wsUtil:encodeRange({Offset, Length}, Size),
  217. sendFile(Socket, 206, lists:append(ResponseHeaders, [{?CONTENT_LENGTH_HEADER, Length}, {<<"Content-Range">>, ERange}]), Filename, {Offset, Length});
  218. invalid_range ->
  219. ERange = wsUtil:encodeRange(invalid_range, Size),
  220. sendResponse(Socket, Method, 416, lists:append(ResponseHeaders, [{<<"Content-Length">>, 0}, {<<"Content-Range">>, ERange}]), <<>>),
  221. {error, range}
  222. end,
  223. case Ret of
  224. ok ->
  225. closeOrKeepAlive(UserHeaders, TemHeader);
  226. {error, Reason} ->
  227. {stop, Reason}
  228. end
  229. end.
  230. %% @doc Generate a HTTP response and send it to the client.
  231. sendResponse(Socket, Method, Code, Headers, UserBody) ->
  232. Body =
  233. case Method of
  234. 'HEAD' ->
  235. <<>>;
  236. _ ->
  237. case Code of
  238. 304 ->
  239. <<>>;
  240. 204 ->
  241. <<>>;
  242. _ ->
  243. UserBody
  244. end
  245. end,
  246. Response = httpResponse(Code, Headers, Body),
  247. case wsNet:send(Socket, Response) of
  248. ok ->
  249. ok;
  250. _Err ->
  251. ?wsErr("send_response error ~p~n", [_Err])
  252. end.
  253. %% @doc Send a HTTP response to the client where the body is the
  254. %% contents of the given file. Assumes correctly set response code
  255. %% and headers.
  256. -spec sendFile(Req, Code, Headers, Filename, Range) -> ok when
  257. Req :: elli:wsReq(),
  258. Code :: elli:httpCode(),
  259. Headers :: elli:headers(),
  260. Filename :: file:filename(),
  261. Range :: wsUtil:range().
  262. sendFile(Socket, Code, Headers, Filename, Range) ->
  263. ResponseHeaders = httpResponse(Code, Headers, <<>>),
  264. case file:open(Filename, [read, raw, binary]) of
  265. {ok, Fd} -> doSendFile(Fd, Range, Socket, ResponseHeaders);
  266. {error, _FileError} = Err ->
  267. sendSrvError(Socket),
  268. Err
  269. end.
  270. doSendFile(Fd, {Offset, Length}, Socket, Headers) ->
  271. try wsNet:send(Socket, Headers) of
  272. ok ->
  273. case wsNet:sendfile(Fd, Socket, Offset, Length, []) of
  274. {ok, _BytesSent} -> ok;
  275. {error, Closed} = LErr when Closed =:= closed orelse Closed =:= enotconn ->
  276. ?wsErr("send file error"),
  277. LErr
  278. end;
  279. {error, Closed} = LErr when Closed =:= closed orelse Closed =:= enotconn ->
  280. ?wsErr("send file error"),
  281. LErr
  282. after
  283. file:close(Fd)
  284. end.
  285. %% @doc To send a response, we must first have received everything the
  286. %% client is sending. If this is not the case, {@link send_bad_request/1}
  287. %% might reset the client connection.
  288. sendBadRequest(Socket) ->
  289. sendRescueResponse(Socket, 400, <<"Bad Request">>).
  290. sendSrvError(Socket) ->
  291. sendRescueResponse(Socket, 500, <<"Server Error">>).
  292. sendRescueResponse(Socket, Code, Body) ->
  293. Response = httpResponse(Code, Body),
  294. wsNet:send(Socket, Response).
  295. %% CHUNKED-TRANSFER
  296. %% @doc The chunk loop is an intermediary between the socket and the
  297. %% user. We forward anything the user sends until the user sends an
  298. %% empty response, which signals that the connection should be
  299. %% closed. When the client closes the socket, the loop exits.
  300. startChunkLoop(Socket) ->
  301. %% Set the socket to active so we receive the tcp_closed message
  302. %% if the client closes the connection
  303. wsNet:setopts(Socket, [{active, once}]),
  304. ?MODULE:chunkLoop(Socket).
  305. chunkLoop(Socket) ->
  306. receive
  307. {tcp_closed, Socket} ->
  308. {error, client_closed};
  309. {chunk, close} ->
  310. case wsNet:send(Socket, <<"0\r\n\r\n">>) of
  311. ok ->
  312. ok;
  313. {error, Closed} when Closed =:= closed orelse Closed =:= enotconn ->
  314. {error, client_closed}
  315. end;
  316. {chunk, close, From} ->
  317. case wsNet:send(Socket, <<"0\r\n\r\n">>) of
  318. ok ->
  319. From ! {self(), ok},
  320. ok;
  321. {error, Closed} when Closed =:= closed orelse Closed =:= enotconn ->
  322. From ! {self(), {error, closed}},
  323. ok
  324. end;
  325. {chunk, Data} ->
  326. sendChunk(Socket, Data),
  327. ?MODULE:chunkLoop(Socket);
  328. {chunk, Data, From} ->
  329. case sendChunk(Socket, Data) of
  330. ok ->
  331. From ! {self(), ok};
  332. {error, Closed} when Closed =:= closed orelse Closed =:= enotconn ->
  333. From ! {self(), {error, closed}}
  334. end,
  335. ?MODULE:chunkLoop(Socket)
  336. after 10000 ->
  337. ?MODULE:chunkLoop(Socket)
  338. end.
  339. sendChunk(Socket, Data) ->
  340. case iolist_size(Data) of
  341. 0 -> ok;
  342. Size ->
  343. Response = [integer_to_binary(Size, 16), <<"\r\n">>, Data, <<"\r\n">>],
  344. wsNet:send(Socket, Response)
  345. end.
  346. maybeSendContinue(Socket, Headers) ->
  347. % According to RFC2616 section 8.2.3 an origin server must respond with
  348. % either a "100 Continue" or a final response code when the client
  349. % headers contains "Expect:100-continue"
  350. case lists:keyfind(?EXPECT_HEADER, 1, Headers) of
  351. <<"100-continue">> ->
  352. Response = httpResponse(100),
  353. wsNet:send(Socket, Response);
  354. _Other ->
  355. ok
  356. end.
  357. httpResponse(Code) ->
  358. httpResponse(Code, <<>>).
  359. httpResponse(Code, Body) ->
  360. httpResponse(Code, [{?CONTENT_LENGTH_HEADER, size(Body)}], Body).
  361. httpResponse(Code, Headers, Body) ->
  362. [<<"HTTP/1.1 ">>, status(Code), <<"\r\n">>, spellHeaders(Headers), <<"\r\n">>, Body].
  363. spellHeaders(Headers) ->
  364. <<<<(toBinStr(Key))/binary, ": ", (toBinStr(Value))/binary, "\r\n">> || {Key, Value} <- Headers>>.
  365. -spec splitArgs(binary()) -> list({binary(), binary() | true}).
  366. splitArgs(<<>>) -> [];
  367. splitArgs(Qs) ->
  368. Tokens = binary:split(Qs, <<"&">>, [global, trim]),
  369. [case binary:split(Token, <<"=">>) of [Token] -> {Token, true}; [Name, Value] ->
  370. {Name, Value} end || Token <- Tokens].
  371. toBinStr(V) when is_integer(V) -> integer_to_binary(V);
  372. toBinStr(V) when is_binary(V) -> V;
  373. toBinStr(V) when is_list(V) -> list_to_binary(V);
  374. toBinStr(V) when is_atom(V) -> atom_to_binary(V).
  375. closeOrKeepAlive(UserHeaders, ReqHeader) ->
  376. case lists:keyfind(?CONNECTION_HEADER, 1, UserHeaders) of
  377. {_, <<"Close">>} ->
  378. close;
  379. {_, <<"close">>} ->
  380. close;
  381. _ ->
  382. case lists:keyfind(?CONNECTION_HEADER, 1, ReqHeader) of
  383. {_, <<"Close">>} ->
  384. close;
  385. {_, <<"close">>} ->
  386. close;
  387. _ ->
  388. keep_alive
  389. end
  390. end.
  391. connection(UserHeaders, ReqHeader) ->
  392. case lists:keyfind(?CONNECTION_HEADER, 1, UserHeaders) of
  393. false ->
  394. case lists:keyfind(?CONNECTION_HEADER, 1, ReqHeader) of
  395. false ->
  396. {?CONNECTION_HEADER, <<"Keep-Alive">>};
  397. Tuple ->
  398. Tuple
  399. end;
  400. _ ->
  401. []
  402. end.
  403. contentLength(Headers, Body) ->
  404. case lists:keyfind(?CONTENT_LENGTH_HEADER, Headers) of
  405. false ->
  406. {?CONTENT_LENGTH_HEADER, iolist_size(Body)};
  407. _ ->
  408. []
  409. end.
  410. transferEncoding(Headers) ->
  411. case lists:keyfind(?TRANSFER_ENCODING_HEADER, Headers) of
  412. false ->
  413. {?TRANSFER_ENCODING_HEADER, <<"chunked">>};
  414. _ ->
  415. []
  416. end.
  417. %% HTTP STATUS CODES
  418. status(100) -> <<"100 Continue">>;
  419. status(101) -> <<"101 Switching Protocols">>;
  420. status(102) -> <<"102 Processing">>;
  421. status(200) -> <<"200 OK">>;
  422. status(201) -> <<"201 Created">>;
  423. status(202) -> <<"202 Accepted">>;
  424. status(203) -> <<"203 Non-Authoritative Information">>;
  425. status(204) -> <<"204 No Content">>;
  426. status(205) -> <<"205 Reset Content">>;
  427. status(206) -> <<"206 Partial Content">>;
  428. status(207) -> <<"207 Multi-Status">>;
  429. status(226) -> <<"226 IM Used">>;
  430. status(300) -> <<"300 Multiple Choices">>;
  431. status(301) -> <<"301 Moved Permanently">>;
  432. status(302) -> <<"302 Found">>;
  433. status(303) -> <<"303 See Other">>;
  434. status(304) -> <<"304 Not Modified">>;
  435. status(305) -> <<"305 Use Proxy">>;
  436. status(306) -> <<"306 Switch Proxy">>;
  437. status(307) -> <<"307 Temporary Redirect">>;
  438. status(400) -> <<"400 Bad Request">>;
  439. status(401) -> <<"401 Unauthorized">>;
  440. status(402) -> <<"402 Payment Required">>;
  441. status(403) -> <<"403 Forbidden">>;
  442. status(404) -> <<"404 Not Found">>;
  443. status(405) -> <<"405 Method Not Allowed">>;
  444. status(406) -> <<"406 Not Acceptable">>;
  445. status(407) -> <<"407 Proxy Authentication Required">>;
  446. status(408) -> <<"408 Request Timeout">>;
  447. status(409) -> <<"409 Conflict">>;
  448. status(410) -> <<"410 Gone">>;
  449. status(411) -> <<"411 Length Required">>;
  450. status(412) -> <<"412 Precondition Failed">>;
  451. status(413) -> <<"413 Request Entity Too Large">>;
  452. status(414) -> <<"414 Request-URI Too Long">>;
  453. status(415) -> <<"415 Unsupported Media Type">>;
  454. status(416) -> <<"416 Requested Range Not Satisfiable">>;
  455. status(417) -> <<"417 Expectation Failed">>;
  456. status(418) -> <<"418 I'm a teapot">>;
  457. status(422) -> <<"422 Unprocessable Entity">>;
  458. status(423) -> <<"423 Locked">>;
  459. status(424) -> <<"424 Failed Dependency">>;
  460. status(425) -> <<"425 Unordered Collection">>;
  461. status(426) -> <<"426 Upgrade Required">>;
  462. status(428) -> <<"428 Precondition Required">>;
  463. status(429) -> <<"429 Too Many Requests">>;
  464. status(431) -> <<"431 Request Header Fields Too Large">>;
  465. status(500) -> <<"500 Internal Server Error">>;
  466. status(501) -> <<"501 Not Implemented">>;
  467. status(502) -> <<"502 Bad Gateway">>;
  468. status(503) -> <<"503 Service Unavailable">>;
  469. status(504) -> <<"504 Gateway Timeout">>;
  470. status(505) -> <<"505 HTTP Version Not Supported">>;
  471. status(506) -> <<"506 Variant Also Negotiates">>;
  472. status(507) -> <<"507 Insufficient Storage">>;
  473. status(510) -> <<"510 Not Extended">>;
  474. status(511) -> <<"511 Network Authentication Required">>;
  475. status(I) when is_integer(I), I >= 100, I < 1000 -> <<(integer_to_binary(I))/binary, "Status">>;
  476. status(B) when is_binary(B) -> B.