cow_http_hd.erl 124 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380
  1. %% Copyright (c) 2014-2015, 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(cow_http_hd).
  15. %% Parsing.
  16. -export([parse_accept/1]).
  17. -export([parse_accept_charset/1]).
  18. % @todo -export([parse_accept_datetime/1]). RFC7089
  19. -export([parse_accept_encoding/1]).
  20. % @todo -export([parse_accept_features/1]). RFC2295
  21. -export([parse_accept_language/1]).
  22. -export([parse_accept_ranges/1]).
  23. % @todo -export([parse_access_control_allow_credentials/1]). CORS
  24. % @todo -export([parse_access_control_allow_headers/1]). CORS
  25. % @todo -export([parse_access_control_allow_methods/1]). CORS
  26. % @todo -export([parse_access_control_allow_origin/1]). CORS
  27. % @todo -export([parse_access_control_expose_headers/1]). CORS
  28. % @todo -export([parse_access_control_max_age/1]). CORS
  29. -export([parse_access_control_request_headers/1]).
  30. -export([parse_access_control_request_method/1]).
  31. -export([parse_age/1]).
  32. -export([parse_allow/1]).
  33. % @todo -export([parse_alternates/1]). RFC2295
  34. % @todo -export([parse_authentication_info/1]). RFC2617
  35. -export([parse_authorization/1]).
  36. -export([parse_cache_control/1]).
  37. -export([parse_connection/1]).
  38. % @todo -export([parse_content_disposition/1]). RFC6266
  39. -export([parse_content_encoding/1]).
  40. -export([parse_content_language/1]).
  41. -export([parse_content_length/1]).
  42. % @todo -export([parse_content_location/1]). RFC7231
  43. % @todo -export([parse_content_md5/1]). RFC2616 (deprecated)
  44. -export([parse_content_range/1]).
  45. % @todo -export([parse_content_security_policy/1]). CSP
  46. % @todo -export([parse_content_security_policy_report_only/1]). CSP
  47. -export([parse_content_type/1]).
  48. % @todo -export([parse_cookie/1]). RFC6265
  49. -export([parse_date/1]).
  50. % @todo -export([parse_digest/1]). RFC3230
  51. % @todo -export([parse_dnt/1]). http://donottrack.us/
  52. -export([parse_etag/1]).
  53. -export([parse_expect/1]).
  54. -export([parse_expires/1]).
  55. % @todo -export([parse_forwarded/1]). RFC7239
  56. % @todo -export([parse_from/1]). RFC7231
  57. -export([parse_host/1]).
  58. -export([parse_http2_settings/1]).
  59. -export([parse_if_match/1]).
  60. -export([parse_if_modified_since/1]).
  61. -export([parse_if_none_match/1]).
  62. -export([parse_if_range/1]).
  63. -export([parse_if_unmodified_since/1]).
  64. % @todo -export([parse_last_event_id/1]). eventsource
  65. -export([parse_last_modified/1]).
  66. % @todo -export([parse_link/1]). RFC5988
  67. % @todo -export([parse_location/1]). RFC7231
  68. -export([parse_max_forwards/1]).
  69. % @todo -export([parse_memento_datetime/1]). RFC7089
  70. % @todo -export([parse_negotiate/1]). RFC2295
  71. -export([parse_origin/1]).
  72. -export([parse_pragma/1]).
  73. % @todo -export([parse_prefer/1]). RFC7240
  74. -export([parse_proxy_authenticate/1]).
  75. % @todo -export([parse_proxy_authentication_info/1]). RFC2617
  76. -export([parse_proxy_authorization/1]).
  77. % @todo -export([parse_proxy_support/1]). RFC4559
  78. % @todo -export([parse_public_key_pins/1]). Key Pinning (upcoming)
  79. % @todo -export([parse_public_key_pins_report_only/1]). Key Pinning (upcoming)
  80. -export([parse_range/1]).
  81. % @todo -export([parse_referer/1]). RFC7231
  82. % @todo -export([parse_refresh/1]). Non-standard (examples: "5", "5; url=http://example.com/")
  83. -export([parse_retry_after/1]).
  84. -export([parse_sec_websocket_accept/1]).
  85. -export([parse_sec_websocket_extensions/1]).
  86. -export([parse_sec_websocket_key/1]).
  87. % @todo -export([parse_sec_websocket_origin/1]). Websocket drafts 7 and 8
  88. -export([parse_sec_websocket_protocol_req/1]).
  89. -export([parse_sec_websocket_protocol_resp/1]).
  90. -export([parse_sec_websocket_version_req/1]).
  91. -export([parse_sec_websocket_version_resp/1]).
  92. % @todo -export([parse_server/1]). RFC7231
  93. % @todo -export([parse_set_cookie/1]). RFC6265
  94. % @todo -export([parse_strict_transport_security/1]). RFC6797
  95. % @todo -export([parse_tcn/1]). RFC2295
  96. -export([parse_te/1]).
  97. -export([parse_trailer/1]).
  98. -export([parse_transfer_encoding/1]).
  99. -export([parse_upgrade/1]).
  100. % @todo -export([parse_user_agent/1]). RFC7231
  101. % @todo -export([parse_variant_vary/1]). RFC2295
  102. -export([parse_vary/1]).
  103. % @todo -export([parse_via/1]). RFC7230
  104. % @todo -export([parse_want_digest/1]). RFC3230
  105. % @todo -export([parse_warning/1]). RFC7234
  106. -export([parse_www_authenticate/1]).
  107. % @todo -export([parse_x_content_duration/1]). Gecko/MDN (value: float)
  108. % @todo -export([parse_x_dns_prefetch_control/1]). Various (value: "on"|"off")
  109. -export([parse_x_forwarded_for/1]).
  110. % @todo -export([parse_x_frame_options/1]). RFC7034
  111. %% Building.
  112. -export([access_control_allow_credentials/0]).
  113. -export([access_control_allow_headers/1]).
  114. -export([access_control_allow_methods/1]).
  115. -export([access_control_allow_origin/1]).
  116. -export([access_control_expose_headers/1]).
  117. -type etag() :: {weak | strong, binary()}.
  118. -export_type([etag/0]).
  119. -type media_type() :: {binary(), binary(), [{binary(), binary()}]}.
  120. -export_type([media_type/0]).
  121. -type qvalue() :: 0..1000.
  122. -export_type([qvalue/0]).
  123. -type websocket_version() :: 0..255.
  124. -export_type([websocket_version/0]).
  125. -include("cow_inline.hrl").
  126. -include("cow_parse.hrl").
  127. -ifdef(TEST).
  128. -include_lib("triq/include/triq.hrl").
  129. vector(Min, Max, Dom) -> ?LET(N, choose(Min, Max), vector(N, Dom)).
  130. small_list(Dom) -> vector(0, 10, Dom).
  131. small_non_empty_list(Dom) -> vector(1, 10, Dom).
  132. alpha_chars() -> "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".
  133. alphanum_chars() -> "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".
  134. digit_chars() -> "0123456789".
  135. ows() -> list(elements([$\s, $\t])).
  136. alpha() -> elements(alpha_chars()).
  137. alphanum() -> elements(alphanum_chars()).
  138. digit() -> elements(digit_chars()).
  139. tchar() ->
  140. frequency([
  141. {1, elements([$!, $#, $$, $%, $&, $', $*, $+, $-, $., $^, $_, $`, $|, $~])},
  142. {99, elements(alphanum_chars())}
  143. ]).
  144. token() ->
  145. ?LET(T,
  146. non_empty(list(tchar())),
  147. list_to_binary(T)).
  148. abnf_char() ->
  149. int(1, 127).
  150. vchar() ->
  151. int(33, 126).
  152. obs_text() ->
  153. int(128, 255).
  154. qdtext() ->
  155. frequency([
  156. {99, elements("\t\s!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~")},
  157. {1, obs_text()}
  158. ]).
  159. quoted_pair() ->
  160. [$\\, frequency([
  161. {99, elements("\t\s!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~")},
  162. {1, obs_text()}
  163. ])].
  164. quoted_string() ->
  165. [$", list(frequency([{100, qdtext()}, {1, quoted_pair()}])), $"].
  166. %% Helper function for ( token / quoted-string ) values.
  167. unquote([$", V, $"]) -> unquote(V, <<>>);
  168. unquote(V) -> V.
  169. unquote([], Acc) -> Acc;
  170. unquote([[$\\, C]|Tail], Acc) -> unquote(Tail, << Acc/binary, C >>);
  171. unquote([C|Tail], Acc) -> unquote(Tail, << Acc/binary, C >>).
  172. parameter() ->
  173. ?SUCHTHAT({K, _, _, _},
  174. {token(), oneof([token(), quoted_string()]), ows(), ows()},
  175. K =/= <<"q">>).
  176. weight() ->
  177. frequency([
  178. {90, int(0, 1000)},
  179. {10, undefined}
  180. ]).
  181. %% Helper function for weight's qvalue formatting.
  182. qvalue_to_iodata(0) -> <<"0">>;
  183. qvalue_to_iodata(Q) when Q < 10 -> [<<"0.00">>, integer_to_binary(Q)];
  184. qvalue_to_iodata(Q) when Q < 100 -> [<<"0.0">>, integer_to_binary(Q)];
  185. qvalue_to_iodata(Q) when Q < 1000 -> [<<"0.">>, integer_to_binary(Q)];
  186. qvalue_to_iodata(1000) -> <<"1">>.
  187. -endif.
  188. %% Parsing.
  189. %% @doc Parse the Accept header.
  190. -spec parse_accept(binary()) -> [{media_type(), qvalue(), [binary() | {binary(), binary()}]}].
  191. parse_accept(<<"*/*">>) ->
  192. [{{<<"*">>, <<"*">>, []}, 1000, []}];
  193. parse_accept(Accept) ->
  194. media_range_list(Accept, []).
  195. media_range_list(<< C, R/bits >>, Acc) when ?IS_TOKEN(C) -> ?LOWER(media_range_type, R, Acc, <<>>);
  196. media_range_list(<< C, R/bits >>, Acc) when ?IS_WS_COMMA(C) -> media_range_list(R, Acc);
  197. media_range_list(<<>>, Acc) -> lists:reverse(Acc).
  198. media_range_type(<< C, R/bits >>, Acc, T) when ?IS_TOKEN(C) -> ?LOWER(media_range_type, R, Acc, T);
  199. media_range_type(<< $/, C, R/bits >>, Acc, T) when ?IS_TOKEN(C) -> ?LOWER(media_range_subtype, R, Acc, T, <<>>);
  200. %% Special clause for badly behaving user agents that send * instead of */*.
  201. media_range_type(<< $;, R/bits >>, Acc, <<"*">>) -> media_range_before_param(R, Acc, <<"*">>, <<"*">>, []).
  202. media_range_subtype(<< C, R/bits >>, Acc, T, S) when ?IS_TOKEN(C) -> ?LOWER(media_range_subtype, R, Acc, T, S);
  203. media_range_subtype(R, Acc, T, S) -> media_range_param_sep(R, Acc, T, S, []).
  204. media_range_param_sep(<<>>, Acc, T, S, P) -> lists:reverse([{{T, S, lists:reverse(P)}, 1000, []}|Acc]);
  205. media_range_param_sep(<< $,, R/bits >>, Acc, T, S, P) -> media_range_list(R, [{{T, S, lists:reverse(P)}, 1000, []}|Acc]);
  206. media_range_param_sep(<< $;, R/bits >>, Acc, T, S, P) -> media_range_before_param(R, Acc, T, S, P);
  207. media_range_param_sep(<< C, R/bits >>, Acc, T, S, P) when ?IS_WS(C) -> media_range_param_sep(R, Acc, T, S, P).
  208. media_range_before_param(<< C, R/bits >>, Acc, T, S, P) when ?IS_WS(C) -> media_range_before_param(R, Acc, T, S, P);
  209. media_range_before_param(<< $q, $=, R/bits >>, Acc, T, S, P) -> media_range_weight(R, Acc, T, S, P);
  210. media_range_before_param(<< C, R/bits >>, Acc, T, S, P) when ?IS_TOKEN(C) -> ?LOWER(media_range_param, R, Acc, T, S, P, <<>>).
  211. media_range_param(<< $=, $", R/bits >>, Acc, T, S, P, K) -> media_range_quoted(R, Acc, T, S, P, K, <<>>);
  212. media_range_param(<< $=, C, R/bits >>, Acc, T, S, P, K) when ?IS_TOKEN(C) -> media_range_value(R, Acc, T, S, P, K, << C >>);
  213. media_range_param(<< C, R/bits >>, Acc, T, S, P, K) when ?IS_TOKEN(C) -> ?LOWER(media_range_param, R, Acc, T, S, P, K).
  214. media_range_quoted(<< $", R/bits >>, Acc, T, S, P, K, V) -> media_range_param_sep(R, Acc, T, S, [{K, V}|P]);
  215. media_range_quoted(<< $\\, C, R/bits >>, Acc, T, S, P, K, V) when ?IS_VCHAR_OBS(C) -> media_range_quoted(R, Acc, T, S, P, K, << V/binary, C >>);
  216. media_range_quoted(<< C, R/bits >>, Acc, T, S, P, K, V) when ?IS_VCHAR_OBS(C) -> media_range_quoted(R, Acc, T, S, P, K, << V/binary, C >>).
  217. media_range_value(<< C, R/bits >>, Acc, T, S, P, K, V) when ?IS_TOKEN(C) -> media_range_value(R, Acc, T, S, P, K, << V/binary, C >>);
  218. media_range_value(R, Acc, T, S, P, K, V) -> media_range_param_sep(R, Acc, T, S, [{K, V}|P]).
  219. media_range_weight(<< "1.000", R/bits >>, Acc, T, S, P) -> accept_ext_sep(R, Acc, T, S, P, 1000, []);
  220. media_range_weight(<< "1.00", R/bits >>, Acc, T, S, P) -> accept_ext_sep(R, Acc, T, S, P, 1000, []);
  221. media_range_weight(<< "1.0", R/bits >>, Acc, T, S, P) -> accept_ext_sep(R, Acc, T, S, P, 1000, []);
  222. media_range_weight(<< "1.", R/bits >>, Acc, T, S, P) -> accept_ext_sep(R, Acc, T, S, P, 1000, []);
  223. media_range_weight(<< "1", R/bits >>, Acc, T, S, P) -> accept_ext_sep(R, Acc, T, S, P, 1000, []);
  224. media_range_weight(<< "0.", A, B, C, R/bits >>, Acc, T, S, P) when ?IS_DIGIT(A), ?IS_DIGIT(B), ?IS_DIGIT(C) ->
  225. accept_ext_sep(R, Acc, T, S, P, (A - $0) * 100 + (B - $0) * 10 + (C - $0), []);
  226. media_range_weight(<< "0.", A, B, R/bits >>, Acc, T, S, P) when ?IS_DIGIT(A), ?IS_DIGIT(B) ->
  227. accept_ext_sep(R, Acc, T, S, P, (A - $0) * 100 + (B - $0) * 10, []);
  228. media_range_weight(<< "0.", A, R/bits >>, Acc, T, S, P) when ?IS_DIGIT(A) ->
  229. accept_ext_sep(R, Acc, T, S, P, (A - $0) * 100, []);
  230. media_range_weight(<< "0.", R/bits >>, Acc, T, S, P) -> accept_ext_sep(R, Acc, T, S, P, 0, []);
  231. media_range_weight(<< "0", R/bits >>, Acc, T, S, P) -> accept_ext_sep(R, Acc, T, S, P, 0, []);
  232. %% Special clauses for badly behaving user agents that send .123 instead of 0.123.
  233. media_range_weight(<< ".", A, B, C, R/bits >>, Acc, T, S, P) when ?IS_DIGIT(A), ?IS_DIGIT(B), ?IS_DIGIT(C) ->
  234. accept_ext_sep(R, Acc, T, S, P, (A - $0) * 100 + (B - $0) * 10 + (C - $0), []);
  235. media_range_weight(<< ".", A, B, R/bits >>, Acc, T, S, P) when ?IS_DIGIT(A), ?IS_DIGIT(B) ->
  236. accept_ext_sep(R, Acc, T, S, P, (A - $0) * 100 + (B - $0) * 10, []);
  237. media_range_weight(<< ".", A, R/bits >>, Acc, T, S, P) when ?IS_DIGIT(A) ->
  238. accept_ext_sep(R, Acc, T, S, P, (A - $0) * 100, []).
  239. accept_ext_sep(<<>>, Acc, T, S, P, Q, E) -> lists:reverse([{{T, S, lists:reverse(P)}, Q, lists:reverse(E)}|Acc]);
  240. accept_ext_sep(<< $,, R/bits >>, Acc, T, S, P, Q, E) -> media_range_list(R, [{{T, S, lists:reverse(P)}, Q, lists:reverse(E)}|Acc]);
  241. accept_ext_sep(<< $;, R/bits >>, Acc, T, S, P, Q, E) -> accept_before_ext(R, Acc, T, S, P, Q, E);
  242. accept_ext_sep(<< C, R/bits >>, Acc, T, S, P, Q, E) when ?IS_WS(C) -> accept_ext_sep(R, Acc, T, S, P, Q, E).
  243. accept_before_ext(<< C, R/bits >>, Acc, T, S, P, Q, E) when ?IS_WS(C) -> accept_before_ext(R, Acc, T, S, P, Q, E);
  244. accept_before_ext(<< C, R/bits >>, Acc, T, S, P, Q, E) when ?IS_TOKEN(C) -> ?LOWER(accept_ext, R, Acc, T, S, P, Q, E, <<>>).
  245. accept_ext(<< $=, $", R/bits >>, Acc, T, S, P, Q, E, K) -> accept_quoted(R, Acc, T, S, P, Q, E, K, <<>>);
  246. accept_ext(<< $=, C, R/bits >>, Acc, T, S, P, Q, E, K) when ?IS_TOKEN(C) -> accept_value(R, Acc, T, S, P, Q, E, K, << C >>);
  247. accept_ext(<< C, R/bits >>, Acc, T, S, P, Q, E, K) when ?IS_TOKEN(C) -> ?LOWER(accept_ext, R, Acc, T, S, P, Q, E, K);
  248. accept_ext(R, Acc, T, S, P, Q, E, K) -> accept_ext_sep(R, Acc, T, S, P, Q, [K|E]).
  249. accept_quoted(<< $", R/bits >>, Acc, T, S, P, Q, E, K, V) -> accept_ext_sep(R, Acc, T, S, P, Q, [{K, V}|E]);
  250. accept_quoted(<< $\\, C, R/bits >>, Acc, T, S, P, Q, E, K, V) when ?IS_VCHAR_OBS(C) -> accept_quoted(R, Acc, T, S, P, Q, E, K, << V/binary, C >>);
  251. accept_quoted(<< C, R/bits >>, Acc, T, S, P, Q, E, K, V) when ?IS_VCHAR_OBS(C) -> accept_quoted(R, Acc, T, S, P, Q, E, K, << V/binary, C >>).
  252. accept_value(<< C, R/bits >>, Acc, T, S, P, Q, E, K, V) when ?IS_TOKEN(C) -> accept_value(R, Acc, T, S, P, Q, E, K, << V/binary, C >>);
  253. accept_value(R, Acc, T, S, P, Q, E, K, V) -> accept_ext_sep(R, Acc, T, S, P, Q, [{K, V}|E]).
  254. -ifdef(TEST).
  255. accept_ext() ->
  256. oneof([token(), parameter()]).
  257. accept_params() ->
  258. frequency([
  259. {90, []},
  260. {10, small_list(accept_ext())}
  261. ]).
  262. accept() ->
  263. ?LET({T, S, P, W, E},
  264. {token(), token(), small_list(parameter()), weight(), accept_params()},
  265. {T, S, P, W, E, iolist_to_binary([T, $/, S,
  266. [[OWS1, $;, OWS2, K, $=, V] || {K, V, OWS1, OWS2} <- P],
  267. case W of
  268. undefined -> [];
  269. _ -> [
  270. [<<";q=">>, qvalue_to_iodata(W)],
  271. [case Ext of
  272. {K, V, OWS1, OWS2} -> [OWS1, $;, OWS2, K, $=, V];
  273. K -> [$;, K]
  274. end || Ext <- E]]
  275. end])}
  276. ).
  277. prop_parse_accept() ->
  278. ?FORALL(L,
  279. vector(1, 50, accept()),
  280. begin
  281. << _, Accept/binary >> = iolist_to_binary([[$,, A] || {_, _, _, _, _, A} <- L]),
  282. ResL = parse_accept(Accept),
  283. CheckedL = [begin
  284. ExpectedP = [{?LOWER(K), unquote(V)} || {K, V, _, _} <- P],
  285. ExpectedE = [case Ext of
  286. {K, V, _, _} -> {?LOWER(K), unquote(V)};
  287. K -> ?LOWER(K)
  288. end || Ext <- E],
  289. ResT =:= ?LOWER(T)
  290. andalso ResS =:= ?LOWER(S)
  291. andalso ResP =:= ExpectedP
  292. andalso (ResW =:= W orelse (W =:= undefined andalso ResW =:= 1000))
  293. andalso ((W =:= undefined andalso ResE =:= []) orelse (W =/= undefined andalso ResE =:= ExpectedE))
  294. end || {{T, S, P, W, E, _}, {{ResT, ResS, ResP}, ResW, ResE}} <- lists:zip(L, ResL)],
  295. [true] =:= lists:usort(CheckedL)
  296. end
  297. ).
  298. parse_accept_test_() ->
  299. Tests = [
  300. {<<>>, []},
  301. {<<" ">>, []},
  302. {<<"audio/*; q=0.2, audio/basic">>, [
  303. {{<<"audio">>, <<"*">>, []}, 200, []},
  304. {{<<"audio">>, <<"basic">>, []}, 1000, []}
  305. ]},
  306. {<<"text/plain; q=0.5, text/html, "
  307. "text/x-dvi; q=0.8, text/x-c">>, [
  308. {{<<"text">>, <<"plain">>, []}, 500, []},
  309. {{<<"text">>, <<"html">>, []}, 1000, []},
  310. {{<<"text">>, <<"x-dvi">>, []}, 800, []},
  311. {{<<"text">>, <<"x-c">>, []}, 1000, []}
  312. ]},
  313. {<<"text/*, text/html, text/html;level=1, */*">>, [
  314. {{<<"text">>, <<"*">>, []}, 1000, []},
  315. {{<<"text">>, <<"html">>, []}, 1000, []},
  316. {{<<"text">>, <<"html">>, [{<<"level">>, <<"1">>}]}, 1000, []},
  317. {{<<"*">>, <<"*">>, []}, 1000, []}
  318. ]},
  319. {<<"text/*;q=0.3, text/html;q=0.7, text/html;level=1, "
  320. "text/html;level=2;q=0.4, */*;q=0.5">>, [
  321. {{<<"text">>, <<"*">>, []}, 300, []},
  322. {{<<"text">>, <<"html">>, []}, 700, []},
  323. {{<<"text">>, <<"html">>, [{<<"level">>, <<"1">>}]}, 1000, []},
  324. {{<<"text">>, <<"html">>, [{<<"level">>, <<"2">>}]}, 400, []},
  325. {{<<"*">>, <<"*">>, []}, 500, []}
  326. ]},
  327. {<<"text/html;level=1;quoted=\"hi hi hi\";"
  328. "q=0.123;standalone;complex=gits, text/plain">>, [
  329. {{<<"text">>, <<"html">>,
  330. [{<<"level">>, <<"1">>}, {<<"quoted">>, <<"hi hi hi">>}]}, 123,
  331. [<<"standalone">>, {<<"complex">>, <<"gits">>}]},
  332. {{<<"text">>, <<"plain">>, []}, 1000, []}
  333. ]},
  334. {<<"text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2">>, [
  335. {{<<"text">>, <<"html">>, []}, 1000, []},
  336. {{<<"image">>, <<"gif">>, []}, 1000, []},
  337. {{<<"image">>, <<"jpeg">>, []}, 1000, []},
  338. {{<<"*">>, <<"*">>, []}, 200, []},
  339. {{<<"*">>, <<"*">>, []}, 200, []}
  340. ]}
  341. ],
  342. [{V, fun() -> R = parse_accept(V) end} || {V, R} <- Tests].
  343. parse_accept_error_test_() ->
  344. Tests = [
  345. <<"audio/basic, */;q=0.5">>,
  346. <<"audio/, audio/basic">>,
  347. <<"aud\tio/basic">>,
  348. <<"audio/basic;t=\"zero \\", 0, " woo\"">>
  349. ],
  350. [{V, fun() -> {'EXIT', _} = (catch parse_accept(V)) end} || V <- Tests].
  351. horse_parse_accept() ->
  352. horse:repeat(20000,
  353. parse_accept(<<"text/*;q=0.3, text/html;q=0.7, text/html;level=1, "
  354. "text/html;level=2;q=0.4, */*;q=0.5">>)
  355. ).
  356. -endif.
  357. %% @doc Parse the Accept-Charset header.
  358. -spec parse_accept_charset(binary()) -> [{binary(), qvalue()}].
  359. parse_accept_charset(Charset) ->
  360. nonempty(conneg_list(Charset, [])).
  361. conneg_list(<<>>, Acc) -> lists:reverse(Acc);
  362. conneg_list(<< C, R/bits >>, Acc) when ?IS_WS_COMMA(C) -> conneg_list(R, Acc);
  363. conneg_list(<< C, R/bits >>, Acc) when ?IS_TOKEN(C) -> ?LOWER(conneg, R, Acc, <<>>).
  364. conneg(<< C, R/bits >>, Acc, T) when ?IS_TOKEN(C) -> ?LOWER(conneg, R, Acc, T);
  365. conneg(R, Acc, T) -> conneg_param_sep(R, Acc, T).
  366. conneg_param_sep(<<>>, Acc, T) -> lists:reverse([{T, 1000}|Acc]);
  367. conneg_param_sep(<< $,, R/bits >>, Acc, T) -> conneg_list(R, [{T, 1000}|Acc]);
  368. conneg_param_sep(<< $;, R/bits >>, Acc, T) -> conneg_before_weight(R, Acc, T);
  369. conneg_param_sep(<< C, R/bits >>, Acc, T) when ?IS_WS(C) -> conneg_param_sep(R, Acc, T).
  370. conneg_before_weight(<< C, R/bits >>, Acc, T) when ?IS_WS(C) -> conneg_before_weight(R, Acc, T);
  371. conneg_before_weight(<< $q, $=, R/bits >>, Acc, T) -> conneg_weight(R, Acc, T);
  372. %% Special clause for broken user agents that confuse ; and , separators.
  373. conneg_before_weight(<< C, R/bits >>, Acc, T) when ?IS_TOKEN(C) -> ?LOWER(conneg, R, [{T, 1000}|Acc], <<>>).
  374. conneg_weight(<< "1.000", R/bits >>, Acc, T) -> conneg_list_sep(R, [{T, 1000}|Acc]);
  375. conneg_weight(<< "1.00", R/bits >>, Acc, T) -> conneg_list_sep(R, [{T, 1000}|Acc]);
  376. conneg_weight(<< "1.0", R/bits >>, Acc, T) -> conneg_list_sep(R, [{T, 1000}|Acc]);
  377. conneg_weight(<< "1.", R/bits >>, Acc, T) -> conneg_list_sep(R, [{T, 1000}|Acc]);
  378. conneg_weight(<< "1", R/bits >>, Acc, T) -> conneg_list_sep(R, [{T, 1000}|Acc]);
  379. conneg_weight(<< "0.", A, B, C, R/bits >>, Acc, T) when ?IS_DIGIT(A), ?IS_DIGIT(B), ?IS_DIGIT(C) ->
  380. conneg_list_sep(R, [{T, (A - $0) * 100 + (B - $0) * 10 + (C - $0)}|Acc]);
  381. conneg_weight(<< "0.", A, B, R/bits >>, Acc, T) when ?IS_DIGIT(A), ?IS_DIGIT(B) ->
  382. conneg_list_sep(R, [{T, (A - $0) * 100 + (B - $0) * 10}|Acc]);
  383. conneg_weight(<< "0.", A, R/bits >>, Acc, T) when ?IS_DIGIT(A) ->
  384. conneg_list_sep(R, [{T, (A - $0) * 100}|Acc]);
  385. conneg_weight(<< "0.", R/bits >>, Acc, T) -> conneg_list_sep(R, [{T, 0}|Acc]);
  386. conneg_weight(<< "0", R/bits >>, Acc, T) -> conneg_list_sep(R, [{T, 0}|Acc]).
  387. conneg_list_sep(<<>>, Acc) -> lists:reverse(Acc);
  388. conneg_list_sep(<< C, R/bits >>, Acc) when ?IS_WS(C) -> conneg_list_sep(R, Acc);
  389. conneg_list_sep(<< $,, R/bits >>, Acc) -> conneg_list(R, Acc).
  390. -ifdef(TEST).
  391. accept_charset() ->
  392. ?LET({C, W},
  393. {token(), weight()},
  394. {C, W, iolist_to_binary([C, case W of
  395. undefined -> [];
  396. _ -> [<<";q=">>, qvalue_to_iodata(W)]
  397. end])}
  398. ).
  399. prop_parse_accept_charset() ->
  400. ?FORALL(L,
  401. non_empty(list(accept_charset())),
  402. begin
  403. << _, AcceptCharset/binary >> = iolist_to_binary([[$,, A] || {_, _, A} <- L]),
  404. ResL = parse_accept_charset(AcceptCharset),
  405. CheckedL = [begin
  406. ResC =:= ?LOWER(Ch)
  407. andalso (ResW =:= W orelse (W =:= undefined andalso ResW =:= 1000))
  408. end || {{Ch, W, _}, {ResC, ResW}} <- lists:zip(L, ResL)],
  409. [true] =:= lists:usort(CheckedL)
  410. end).
  411. parse_accept_charset_test_() ->
  412. Tests = [
  413. {<<"iso-8859-5, unicode-1-1;q=0.8">>, [
  414. {<<"iso-8859-5">>, 1000},
  415. {<<"unicode-1-1">>, 800}
  416. ]},
  417. %% Some user agents send this invalid value for the Accept-Charset header
  418. {<<"ISO-8859-1;utf-8;q=0.7,*;q=0.7">>, [
  419. {<<"iso-8859-1">>, 1000},
  420. {<<"utf-8">>, 700},
  421. {<<"*">>, 700}
  422. ]}
  423. ],
  424. [{V, fun() -> R = parse_accept_charset(V) end} || {V, R} <- Tests].
  425. parse_accept_charset_error_test_() ->
  426. Tests = [
  427. <<>>
  428. ],
  429. [{V, fun() -> {'EXIT', _} = (catch parse_accept_charset(V)) end} || V <- Tests].
  430. horse_parse_accept_charset() ->
  431. horse:repeat(20000,
  432. parse_accept_charset(<<"iso-8859-5, unicode-1-1;q=0.8">>)
  433. ).
  434. -endif.
  435. %% @doc Parse the Accept-Encoding header.
  436. -spec parse_accept_encoding(binary()) -> [{binary(), qvalue()}].
  437. parse_accept_encoding(Encoding) ->
  438. conneg_list(Encoding, []).
  439. -ifdef(TEST).
  440. accept_encoding() ->
  441. ?LET({E, W},
  442. {token(), weight()},
  443. {E, W, iolist_to_binary([E, case W of
  444. undefined -> [];
  445. _ -> [<<";q=">>, qvalue_to_iodata(W)]
  446. end])}
  447. ).
  448. %% @todo This property seems useless, see prop_accept_charset.
  449. prop_parse_accept_encoding() ->
  450. ?FORALL(L,
  451. non_empty(list(accept_encoding())),
  452. begin
  453. << _, AcceptEncoding/binary >> = iolist_to_binary([[$,, A] || {_, _, A} <- L]),
  454. ResL = parse_accept_encoding(AcceptEncoding),
  455. CheckedL = [begin
  456. ResE =:= ?LOWER(E)
  457. andalso (ResW =:= W orelse (W =:= undefined andalso ResW =:= 1000))
  458. end || {{E, W, _}, {ResE, ResW}} <- lists:zip(L, ResL)],
  459. [true] =:= lists:usort(CheckedL)
  460. end).
  461. parse_accept_encoding_test_() ->
  462. Tests = [
  463. {<<>>, []},
  464. {<<"*">>, [{<<"*">>, 1000}]},
  465. {<<"compress, gzip">>, [
  466. {<<"compress">>, 1000},
  467. {<<"gzip">>, 1000}
  468. ]},
  469. {<<"compress;q=0.5, gzip;q=1.0">>, [
  470. {<<"compress">>, 500},
  471. {<<"gzip">>, 1000}
  472. ]},
  473. {<<"gzip;q=1.0, identity; q=0.5, *;q=0">>, [
  474. {<<"gzip">>, 1000},
  475. {<<"identity">>, 500},
  476. {<<"*">>, 0}
  477. ]}
  478. ],
  479. [{V, fun() -> R = parse_accept_encoding(V) end} || {V, R} <- Tests].
  480. horse_parse_accept_encoding() ->
  481. horse:repeat(20000,
  482. parse_accept_encoding(<<"gzip;q=1.0, identity; q=0.5, *;q=0">>)
  483. ).
  484. -endif.
  485. %% @doc Parse the Accept-Language header.
  486. -spec parse_accept_language(binary()) -> [{binary(), qvalue()}].
  487. parse_accept_language(LanguageRange) ->
  488. nonempty(language_range_list(LanguageRange, [])).
  489. language_range_list(<<>>, Acc) -> lists:reverse(Acc);
  490. language_range_list(<< C, R/bits >>, Acc) when ?IS_WS_COMMA(C) -> language_range_list(R, Acc);
  491. language_range_list(<< $*, R/bits >>, Acc) -> language_range_param_sep(R, Acc, <<"*">>);
  492. language_range_list(<< C, R/bits >>, Acc) when ?IS_ALPHA(C) ->
  493. ?LOWER(language_range, R, Acc, 1, <<>>).
  494. language_range(<< $-, C, R/bits >>, Acc, _, T) when ?IS_ALPHANUM(C) ->
  495. ?LOWER(language_range_sub, R, Acc, 1, << T/binary, $- >>);
  496. language_range(<< C, R/bits >>, Acc, N, T) when ?IS_ALPHA(C), N < 8 ->
  497. ?LOWER(language_range, R, Acc, N + 1, T);
  498. language_range(R, Acc, _, T) -> language_range_param_sep(R, Acc, T).
  499. language_range_sub(<< $-, R/bits >>, Acc, _, T) -> language_range_sub(R, Acc, 0, << T/binary, $- >>);
  500. language_range_sub(<< C, R/bits >>, Acc, N, T) when ?IS_ALPHANUM(C), N < 8 ->
  501. ?LOWER(language_range_sub, R, Acc, N + 1, T);
  502. language_range_sub(R, Acc, _, T) -> language_range_param_sep(R, Acc, T).
  503. language_range_param_sep(<<>>, Acc, T) -> lists:reverse([{T, 1000}|Acc]);
  504. language_range_param_sep(<< $,, R/bits >>, Acc, T) -> language_range_list(R, [{T, 1000}|Acc]);
  505. language_range_param_sep(<< $;, R/bits >>, Acc, T) -> language_range_before_weight(R, Acc, T);
  506. language_range_param_sep(<< C, R/bits >>, Acc, T) when ?IS_WS(C) -> language_range_param_sep(R, Acc, T).
  507. language_range_before_weight(<< C, R/bits >>, Acc, T) when ?IS_WS(C) -> language_range_before_weight(R, Acc, T);
  508. language_range_before_weight(<< $q, $=, R/bits >>, Acc, T) -> language_range_weight(R, Acc, T);
  509. %% Special clause for broken user agents that confuse ; and , separators.
  510. language_range_before_weight(<< C, R/bits >>, Acc, T) when ?IS_ALPHA(C) ->
  511. ?LOWER(language_range, R, [{T, 1000}|Acc], 1, <<>>).
  512. language_range_weight(<< "1.000", R/bits >>, Acc, T) -> language_range_list_sep(R, [{T, 1000}|Acc]);
  513. language_range_weight(<< "1.00", R/bits >>, Acc, T) -> language_range_list_sep(R, [{T, 1000}|Acc]);
  514. language_range_weight(<< "1.0", R/bits >>, Acc, T) -> language_range_list_sep(R, [{T, 1000}|Acc]);
  515. language_range_weight(<< "1.", R/bits >>, Acc, T) -> language_range_list_sep(R, [{T, 1000}|Acc]);
  516. language_range_weight(<< "1", R/bits >>, Acc, T) -> language_range_list_sep(R, [{T, 1000}|Acc]);
  517. language_range_weight(<< "0.", A, B, C, R/bits >>, Acc, T) when ?IS_DIGIT(A), ?IS_DIGIT(B), ?IS_DIGIT(C) ->
  518. language_range_list_sep(R, [{T, (A - $0) * 100 + (B - $0) * 10 + (C - $0)}|Acc]);
  519. language_range_weight(<< "0.", A, B, R/bits >>, Acc, T) when ?IS_DIGIT(A), ?IS_DIGIT(B) ->
  520. language_range_list_sep(R, [{T, (A - $0) * 100 + (B - $0) * 10}|Acc]);
  521. language_range_weight(<< "0.", A, R/bits >>, Acc, T) when ?IS_DIGIT(A) ->
  522. language_range_list_sep(R, [{T, (A - $0) * 100}|Acc]);
  523. language_range_weight(<< "0.", R/bits >>, Acc, T) -> language_range_list_sep(R, [{T, 0}|Acc]);
  524. language_range_weight(<< "0", R/bits >>, Acc, T) -> language_range_list_sep(R, [{T, 0}|Acc]).
  525. language_range_list_sep(<<>>, Acc) -> lists:reverse(Acc);
  526. language_range_list_sep(<< C, R/bits >>, Acc) when ?IS_WS(C) -> language_range_list_sep(R, Acc);
  527. language_range_list_sep(<< $,, R/bits >>, Acc) -> language_range_list(R, Acc).
  528. -ifdef(TEST).
  529. language_range_tag() ->
  530. vector(1, 8, alpha()).
  531. language_range_subtag() ->
  532. [$-, vector(1, 8, alphanum())].
  533. language_range() ->
  534. [language_range_tag(), small_list(language_range_subtag())].
  535. accept_language() ->
  536. ?LET({R, W},
  537. {language_range(), weight()},
  538. {iolist_to_binary(R), W, iolist_to_binary([R, case W of
  539. undefined -> [];
  540. _ -> [<<";q=">>, qvalue_to_iodata(W)]
  541. end])}
  542. ).
  543. prop_parse_accept_language() ->
  544. ?FORALL(L,
  545. non_empty(list(accept_language())),
  546. begin
  547. << _, AcceptLanguage/binary >> = iolist_to_binary([[$,, A] || {_, _, A} <- L]),
  548. ResL = parse_accept_language(AcceptLanguage),
  549. CheckedL = [begin
  550. ResR =:= ?LOWER(R)
  551. andalso (ResW =:= W orelse (W =:= undefined andalso ResW =:= 1000))
  552. end || {{R, W, _}, {ResR, ResW}} <- lists:zip(L, ResL)],
  553. [true] =:= lists:usort(CheckedL)
  554. end).
  555. parse_accept_language_test_() ->
  556. Tests = [
  557. {<<"da, en-gb;q=0.8, en;q=0.7">>, [
  558. {<<"da">>, 1000},
  559. {<<"en-gb">>, 800},
  560. {<<"en">>, 700}
  561. ]},
  562. {<<"en, en-US, en-cockney, i-cherokee, x-pig-latin, es-419">>, [
  563. {<<"en">>, 1000},
  564. {<<"en-us">>, 1000},
  565. {<<"en-cockney">>, 1000},
  566. {<<"i-cherokee">>, 1000},
  567. {<<"x-pig-latin">>, 1000},
  568. {<<"es-419">>, 1000}
  569. ]}
  570. ],
  571. [{V, fun() -> R = parse_accept_language(V) end} || {V, R} <- Tests].
  572. parse_accept_language_error_test_() ->
  573. Tests = [
  574. <<>>,
  575. <<"loooooong">>,
  576. <<"en-us-loooooong">>,
  577. <<"419-en-us">>
  578. ],
  579. [{V, fun() -> {'EXIT', _} = (catch parse_accept_language(V)) end} || V <- Tests].
  580. horse_parse_accept_language() ->
  581. horse:repeat(20000,
  582. parse_accept_language(<<"da, en-gb;q=0.8, en;q=0.7">>)
  583. ).
  584. -endif.
  585. %% @doc Parse the Accept-Ranges header.
  586. -spec parse_accept_ranges(binary()) -> [binary()].
  587. parse_accept_ranges(<<"none">>) -> [];
  588. parse_accept_ranges(<<"bytes">>) -> [<<"bytes">>];
  589. parse_accept_ranges(AcceptRanges) ->
  590. nonempty(token_ci_list(AcceptRanges, [])).
  591. -ifdef(TEST).
  592. parse_accept_ranges_test_() ->
  593. Tests = [
  594. {<<"bytes">>, [<<"bytes">>]},
  595. {<<"none">>, []},
  596. {<<"bytes, pages, kilos">>, [<<"bytes">>, <<"pages">>, <<"kilos">>]}
  597. ],
  598. [{V, fun() -> R = parse_accept_ranges(V) end} || {V, R} <- Tests].
  599. parse_accept_ranges_error_test_() ->
  600. Tests = [
  601. <<>>
  602. ],
  603. [{V, fun() -> {'EXIT', _} = (catch parse_accept_ranges(V)) end} || V <- Tests].
  604. horse_parse_accept_ranges_none() ->
  605. horse:repeat(200000,
  606. parse_accept_ranges(<<"none">>)
  607. ).
  608. horse_parse_accept_ranges_bytes() ->
  609. horse:repeat(200000,
  610. parse_accept_ranges(<<"bytes">>)
  611. ).
  612. horse_parse_accept_ranges_other() ->
  613. horse:repeat(200000,
  614. parse_accept_ranges(<<"bytes, pages, kilos">>)
  615. ).
  616. -endif.
  617. %% @doc Parse the Access-Control-Request-Headers header.
  618. -spec parse_access_control_request_headers(binary()) -> [binary()].
  619. parse_access_control_request_headers(Headers) ->
  620. token_ci_list(Headers, []).
  621. -ifdef(TEST).
  622. headers() ->
  623. ?LET(L,
  624. list({ows(), ows(), token()}),
  625. case L of
  626. [] -> {[], <<>>};
  627. _ ->
  628. << _, Headers/binary >> = iolist_to_binary([[OWS1, $,, OWS2, M] || {OWS1, OWS2, M} <- L]),
  629. {[?LOWER(M) || {_, _, M} <- L], Headers}
  630. end).
  631. prop_parse_access_control_request_headers() ->
  632. ?FORALL({L, Headers},
  633. headers(),
  634. L =:= parse_access_control_request_headers(Headers)).
  635. parse_access_control_request_headers_test_() ->
  636. Tests = [
  637. {<<>>, []},
  638. {<<"Content-Type">>, [<<"content-type">>]},
  639. {<<"accept, authorization, content-type">>, [<<"accept">>, <<"authorization">>, <<"content-type">>]},
  640. {<<"accept,, , authorization,content-type">>, [<<"accept">>, <<"authorization">>, <<"content-type">>]}
  641. ],
  642. [{V, fun() -> R = parse_access_control_request_headers(V) end} || {V, R} <- Tests].
  643. horse_parse_access_control_request_headers() ->
  644. horse:repeat(200000,
  645. parse_access_control_request_headers(<<"accept, authorization, content-type">>)
  646. ).
  647. -endif.
  648. %% @doc Parse the Access-Control-Request-Method header.
  649. -spec parse_access_control_request_method(binary()) -> binary().
  650. parse_access_control_request_method(Method) ->
  651. true = <<>> =/= Method,
  652. ok = validate_token(Method),
  653. Method.
  654. validate_token(<< C, R/bits >>) when ?IS_TOKEN(C) -> validate_token(R);
  655. validate_token(<<>>) -> ok.
  656. -ifdef(TEST).
  657. parse_access_control_request_method_test_() ->
  658. Tests = [
  659. <<"GET">>,
  660. <<"HEAD">>,
  661. <<"POST">>,
  662. <<"PUT">>,
  663. <<"DELETE">>,
  664. <<"TRACE">>,
  665. <<"CONNECT">>,
  666. <<"whatever">>
  667. ],
  668. [{V, fun() -> R = parse_access_control_request_method(V) end} || {V, R} <- Tests].
  669. parse_access_control_request_method_error_test_() ->
  670. Tests = [
  671. <<>>
  672. ],
  673. [{V, fun() -> {'EXIT', _} = (catch parse_access_control_request_method(V)) end} || V <- Tests].
  674. horse_parse_access_control_request_method() ->
  675. horse:repeat(200000,
  676. parse_access_control_request_method(<<"POST">>)
  677. ).
  678. -endif.
  679. %% @doc Parse the Age header.
  680. -spec parse_age(binary()) -> non_neg_integer().
  681. parse_age(Age) ->
  682. I = binary_to_integer(Age),
  683. true = I >= 0,
  684. I.
  685. -ifdef(TEST).
  686. parse_age_test_() ->
  687. Tests = [
  688. {<<"0">>, 0},
  689. {<<"42">>, 42},
  690. {<<"69">>, 69},
  691. {<<"1337">>, 1337},
  692. {<<"3495">>, 3495},
  693. {<<"1234567890">>, 1234567890}
  694. ],
  695. [{V, fun() -> R = parse_age(V) end} || {V, R} <- Tests].
  696. parse_age_error_test_() ->
  697. Tests = [
  698. <<>>,
  699. <<"123, 123">>,
  700. <<"4.17">>
  701. ],
  702. [{V, fun() -> {'EXIT', _} = (catch parse_age(V)) end} || V <- Tests].
  703. -endif.
  704. %% @doc Parse the Allow header.
  705. -spec parse_allow(binary()) -> [binary()].
  706. parse_allow(Allow) ->
  707. token_list(Allow, []).
  708. -ifdef(TEST).
  709. allow() ->
  710. ?LET(L,
  711. list({ows(), ows(), token()}),
  712. case L of
  713. [] -> {[], <<>>};
  714. _ ->
  715. << _, Allow/binary >> = iolist_to_binary([[OWS1, $,, OWS2, M] || {OWS1, OWS2, M} <- L]),
  716. {[M || {_, _, M} <- L], Allow}
  717. end).
  718. prop_parse_allow() ->
  719. ?FORALL({L, Allow},
  720. allow(),
  721. L =:= parse_allow(Allow)).
  722. parse_allow_test_() ->
  723. Tests = [
  724. {<<>>, []},
  725. {<<"GET, HEAD, PUT">>, [<<"GET">>, <<"HEAD">>, <<"PUT">>]}
  726. ],
  727. [{V, fun() -> R = parse_allow(V) end} || {V, R} <- Tests].
  728. horse_parse_allow() ->
  729. horse:repeat(200000,
  730. parse_allow(<<"GET, HEAD, PUT">>)
  731. ).
  732. -endif.
  733. %% @doc Parse the Authorization header.
  734. %%
  735. %% We support Basic, Digest and Bearer schemes only.
  736. %%
  737. %% In the Digest case we do not validate that the mandatory
  738. %% fields are present. When parsing auth-params, we do not
  739. %% accept BWS characters around the "=".
  740. -spec parse_authorization(binary())
  741. -> {basic, binary(), binary()}
  742. | {bearer, binary()}
  743. | {digest, [{binary(), binary()}]}.
  744. parse_authorization(<<"Basic ", R/bits >>) ->
  745. auth_basic(base64:decode(R), <<>>);
  746. parse_authorization(<<"Bearer ", R/bits >>) when R =/= <<>> ->
  747. validate_auth_bearer(R),
  748. {bearer, R};
  749. parse_authorization(<<"Digest ", R/bits >>) ->
  750. {digest, nonempty(auth_digest_list(R, []))}.
  751. auth_basic(<< $:, Password/bits >>, UserID) -> {basic, UserID, Password};
  752. auth_basic(<< C, R/bits >>, UserID) -> auth_basic(R, << UserID/binary, C >>).
  753. validate_auth_bearer(<< C, R/bits >>) when ?IS_TOKEN68(C) -> validate_auth_bearer(R);
  754. validate_auth_bearer(<< $=, R/bits >>) -> validate_auth_bearer_eq(R);
  755. validate_auth_bearer(<<>>) -> ok.
  756. validate_auth_bearer_eq(<< $=, R/bits >>) -> validate_auth_bearer_eq(R);
  757. validate_auth_bearer_eq(<<>>) -> ok.
  758. auth_digest_list(<<>>, Acc) -> lists:reverse(Acc);
  759. auth_digest_list(<< C, R/bits >>, Acc) when ?IS_WS_COMMA(C) -> auth_digest_list(R, Acc);
  760. auth_digest_list(<< "algorithm=", C, R/bits >>, Acc) when ?IS_TOKEN(C) -> auth_digest_token(R, Acc, <<"algorithm">>, << C >>);
  761. auth_digest_list(<< "cnonce=\"", R/bits >>, Acc) -> auth_digest_quoted(R, Acc, <<"cnonce">>, <<>>);
  762. auth_digest_list(<< "nc=", A, B, C, D, E, F, G, H, R/bits >>, Acc)
  763. when ?IS_LHEX(A), ?IS_LHEX(B), ?IS_LHEX(C), ?IS_LHEX(D),
  764. ?IS_LHEX(E), ?IS_LHEX(F), ?IS_LHEX(G), ?IS_LHEX(H) ->
  765. auth_digest_list_sep(R, [{<<"nc">>, << A, B, C, D, E, F, G, H >>}|Acc]);
  766. auth_digest_list(<< "nonce=\"", R/bits >>, Acc) -> auth_digest_quoted(R, Acc, <<"nonce">>, <<>>);
  767. auth_digest_list(<< "opaque=\"", R/bits >>, Acc) -> auth_digest_quoted(R, Acc, <<"opaque">>, <<>>);
  768. auth_digest_list(<< "qop=", C, R/bits >>, Acc) when ?IS_TOKEN(C) -> auth_digest_token(R, Acc, <<"qop">>, << C >>);
  769. auth_digest_list(<< "realm=\"", R/bits >>, Acc) -> auth_digest_quoted(R, Acc, <<"realm">>, <<>>);
  770. auth_digest_list(<< "response=\"", R/bits >>, Acc) -> auth_digest_quoted(R, Acc, <<"response">>, <<>>);
  771. auth_digest_list(<< "uri=\"", R/bits >>, Acc) -> auth_digest_quoted(R, Acc, <<"uri">>, <<>>);
  772. auth_digest_list(<< "username=\"", R/bits >>, Acc) -> auth_digest_quoted(R, Acc, <<"username">>, <<>>);
  773. auth_digest_list(<< C, R/bits >>, Acc) when ?IS_TOKEN(C) ->
  774. ?LOWER(auth_digest_param, R, Acc, <<>>).
  775. auth_digest_param(<< $=, $", R/bits >>, Acc, K) -> auth_digest_quoted(R, Acc, K, <<>>);
  776. auth_digest_param(<< $=, C, R/bits >>, Acc, K) when ?IS_TOKEN(C) -> auth_digest_token(R, Acc, K, << C >>);
  777. auth_digest_param(<< C, R/bits >>, Acc, K) when ?IS_TOKEN(C) ->
  778. ?LOWER(auth_digest_param, R, Acc, K).
  779. auth_digest_token(<< C, R/bits >>, Acc, K, V) when ?IS_TOKEN(C) -> auth_digest_token(R, Acc, K, << V/binary, C >>);
  780. auth_digest_token(R, Acc, K, V) -> auth_digest_list_sep(R, [{K, V}|Acc]).
  781. auth_digest_quoted(<< $", R/bits >>, Acc, K, V) -> auth_digest_list_sep(R, [{K, V}|Acc]);
  782. auth_digest_quoted(<< $\\, C, R/bits >>, Acc, K, V) when ?IS_VCHAR_OBS(C) -> auth_digest_quoted(R, Acc, K, << V/binary, C >>);
  783. auth_digest_quoted(<< C, R/bits >>, Acc, K, V) when ?IS_VCHAR_OBS(C) -> auth_digest_quoted(R, Acc, K, << V/binary, C >>).
  784. auth_digest_list_sep(<<>>, Acc) -> lists:reverse(Acc);
  785. auth_digest_list_sep(<< $,, R/bits >>, Acc) -> auth_digest_list(R, Acc);
  786. auth_digest_list_sep(<< C, R/bits >>, Acc) when ?IS_WS(C) -> auth_digest_list_sep(R, Acc).
  787. -ifdef(TEST).
  788. parse_authorization_test_() ->
  789. Tests = [
  790. {<<"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==">>, {basic, <<"Aladdin">>, <<"open sesame">>}},
  791. {<<"Bearer mF_9.B5f-4.1JqM">>, {bearer, <<"mF_9.B5f-4.1JqM">>}},
  792. {<<"Digest username=\"Mufasa\","
  793. "realm=\"testrealm@host.com\","
  794. "nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\","
  795. "uri=\"/dir/index.html\","
  796. "qop=auth,"
  797. "nc=00000001,"
  798. "cnonce=\"0a4f113b\","
  799. "response=\"6629fae49393a05397450978507c4ef1\","
  800. "opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"">>,
  801. {digest, [
  802. {<<"username">>, <<"Mufasa">>},
  803. {<<"realm">>, <<"testrealm@host.com">>},
  804. {<<"nonce">>, <<"dcd98b7102dd2f0e8b11d0f600bfb0c093">>},
  805. {<<"uri">>, <<"/dir/index.html">>},
  806. {<<"qop">>, <<"auth">>},
  807. {<<"nc">>, <<"00000001">>},
  808. {<<"cnonce">>, <<"0a4f113b">>},
  809. {<<"response">>, <<"6629fae49393a05397450978507c4ef1">>},
  810. {<<"opaque">>, <<"5ccc069c403ebaf9f0171e9517f40e41">>}]}}
  811. ],
  812. [{V, fun() -> R = parse_authorization(V) end} || {V, R} <- Tests].
  813. horse_parse_authorization_basic() ->
  814. horse:repeat(20000,
  815. parse_authorization(<<"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==">>)
  816. ).
  817. horse_parse_authorization_bearer() ->
  818. horse:repeat(20000,
  819. parse_authorization(<<"Bearer mF_9.B5f-4.1JqM">>)
  820. ).
  821. horse_parse_authorization_digest() ->
  822. horse:repeat(20000,
  823. parse_authorization(
  824. <<"Digest username=\"Mufasa\","
  825. "realm=\"testrealm@host.com\","
  826. "nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\","
  827. "uri=\"/dir/index.html\","
  828. "qop=auth,"
  829. "nc=00000001,"
  830. "cnonce=\"0a4f113b\","
  831. "response=\"6629fae49393a05397450978507c4ef1\","
  832. "opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"">>)
  833. ).
  834. -endif.
  835. %% @doc Parse the Cache-Control header.
  836. %%
  837. %% In the fields list case, we do not support escaping, which shouldn't be needed anyway.
  838. -spec parse_cache_control(binary())
  839. -> [binary() | {binary(), binary()} | {binary(), non_neg_integer()} | {binary(), [binary()]}].
  840. parse_cache_control(<<"no-cache">>) ->
  841. [<<"no-cache">>];
  842. parse_cache_control(<<"max-age=0">>) ->
  843. [{<<"max-age">>, 0}];
  844. parse_cache_control(CacheControl) ->
  845. nonempty(cache_directive_list(CacheControl, [])).
  846. cache_directive_list(<<>>, Acc) -> lists:reverse(Acc);
  847. cache_directive_list(<< C, R/bits >>, Acc) when ?IS_WS_COMMA(C)-> cache_directive_list(R, Acc);
  848. cache_directive_list(<< C, R/bits >>, Acc) when ?IS_TOKEN(C) ->
  849. ?LOWER(cache_directive, R, Acc, <<>>).
  850. cache_directive(<< $=, $", R/bits >>, Acc, T)
  851. when (T =:= <<"no-cache">>) or (T =:= <<"private">>) ->
  852. cache_directive_fields_list(R, Acc, T, []);
  853. cache_directive(<< $=, C, R/bits >>, Acc, T)
  854. when ?IS_DIGIT(C), (T =:= <<"max-age">>) or (T =:= <<"max-stale">>)
  855. or (T =:= <<"min-fresh">>) or (T =:= <<"s-maxage">>) ->
  856. cache_directive_delta(R, Acc, T, (C - $0));
  857. cache_directive(<< $=, $", R/bits >>, Acc, T) -> cache_directive_quoted_string(R, Acc, T, <<>>);
  858. cache_directive(<< $=, C, R/bits >>, Acc, T) when ?IS_TOKEN(C) -> cache_directive_token(R, Acc, T, << C >>);
  859. cache_directive(<< C, R/bits >>, Acc, T) when ?IS_TOKEN(C) ->
  860. ?LOWER(cache_directive, R, Acc, T);
  861. cache_directive(R, Acc, T) -> cache_directive_list_sep(R, [T|Acc]).
  862. cache_directive_delta(<< C, R/bits >>, Acc, K, V) when ?IS_DIGIT(C) -> cache_directive_delta(R, Acc, K, V * 10 + (C - $0));
  863. cache_directive_delta(R, Acc, K, V) -> cache_directive_list_sep(R, [{K, V}|Acc]).
  864. cache_directive_fields_list(<< C, R/bits >>, Acc, K, L) when ?IS_WS_COMMA(C) -> cache_directive_fields_list(R, Acc, K, L);
  865. cache_directive_fields_list(<< $", R/bits >>, Acc, K, L) -> cache_directive_list_sep(R, [{K, lists:reverse(L)}|Acc]);
  866. cache_directive_fields_list(<< C, R/bits >>, Acc, K, L) when ?IS_TOKEN(C) ->
  867. ?LOWER(cache_directive_field, R, Acc, K, L, <<>>).
  868. cache_directive_field(<< C, R/bits >>, Acc, K, L, F) when ?IS_TOKEN(C) ->
  869. ?LOWER(cache_directive_field, R, Acc, K, L, F);
  870. cache_directive_field(R, Acc, K, L, F) -> cache_directive_fields_list_sep(R, Acc, K, [F|L]).
  871. cache_directive_fields_list_sep(<< C, R/bits >>, Acc, K, L) when ?IS_WS(C) -> cache_directive_fields_list_sep(R, Acc, K, L);
  872. cache_directive_fields_list_sep(<< $,, R/bits >>, Acc, K, L) -> cache_directive_fields_list(R, Acc, K, L);
  873. cache_directive_fields_list_sep(<< $", R/bits >>, Acc, K, L) -> cache_directive_list_sep(R, [{K, lists:reverse(L)}|Acc]).
  874. cache_directive_token(<< C, R/bits >>, Acc, K, V) when ?IS_TOKEN(C) -> cache_directive_token(R, Acc, K, << V/binary, C >>);
  875. cache_directive_token(R, Acc, K, V) -> cache_directive_list_sep(R, [{K, V}|Acc]).
  876. cache_directive_quoted_string(<< $", R/bits >>, Acc, K, V) -> cache_directive_list_sep(R, [{K, V}|Acc]);
  877. cache_directive_quoted_string(<< $\\, C, R/bits >>, Acc, K, V) when ?IS_VCHAR_OBS(C) ->
  878. cache_directive_quoted_string(R, Acc, K, << V/binary, C >>);
  879. cache_directive_quoted_string(<< C, R/bits >>, Acc, K, V) when ?IS_VCHAR_OBS(C) ->
  880. cache_directive_quoted_string(R, Acc, K, << V/binary, C >>).
  881. cache_directive_list_sep(<<>>, Acc) -> lists:reverse(Acc);
  882. cache_directive_list_sep(<< C, R/bits >>, Acc) when ?IS_WS(C) -> cache_directive_list_sep(R, Acc);
  883. cache_directive_list_sep(<< $,, R/bits >>, Acc) -> cache_directive_list(R, Acc).
  884. -ifdef(TEST).
  885. cache_directive_unreserved_token() ->
  886. ?SUCHTHAT(T,
  887. token(),
  888. T =/= <<"max-age">> andalso T =/= <<"max-stale">> andalso T =/= <<"min-fresh">>
  889. andalso T =/= <<"s-maxage">> andalso T =/= <<"no-cache">> andalso T =/= <<"private">>).
  890. cache_directive() ->
  891. oneof([
  892. token(),
  893. {cache_directive_unreserved_token(), token()},
  894. {cache_directive_unreserved_token(), quoted_string()},
  895. {elements([<<"max-age">>, <<"max-stale">>, <<"min-fresh">>, <<"s-maxage">>]), non_neg_integer()},
  896. {fields, elements([<<"no-cache">>, <<"private">>]), small_list(token())}
  897. ]).
  898. cache_control() ->
  899. ?LET(L,
  900. non_empty(list(cache_directive())),
  901. begin
  902. << _, CacheControl/binary >> = iolist_to_binary([[$,,
  903. case C of
  904. {fields, K, V} -> [K, $=, $", [[F, $,] || F <- V], $"];
  905. {K, V} when is_integer(V) -> [K, $=, integer_to_binary(V)];
  906. {K, V} -> [K, $=, V];
  907. K -> K
  908. end] || C <- L]),
  909. {L, CacheControl}
  910. end).
  911. prop_parse_cache_control() ->
  912. ?FORALL({L, CacheControl},
  913. cache_control(),
  914. begin
  915. ResL = parse_cache_control(CacheControl),
  916. CheckedL = [begin
  917. ExpectedCc = case Cc of
  918. {fields, K, V} -> {?LOWER(K), [?LOWER(F) || F <- V]};
  919. {K, V} -> {?LOWER(K), unquote(V)};
  920. K -> ?LOWER(K)
  921. end,
  922. ExpectedCc =:= ResCc
  923. end || {Cc, ResCc} <- lists:zip(L, ResL)],
  924. [true] =:= lists:usort(CheckedL)
  925. end).
  926. parse_cache_control_test_() ->
  927. Tests = [
  928. {<<"no-cache">>, [<<"no-cache">>]},
  929. {<<"no-store">>, [<<"no-store">>]},
  930. {<<"max-age=0">>, [{<<"max-age">>, 0}]},
  931. {<<"max-age=30">>, [{<<"max-age">>, 30}]},
  932. {<<"private, community=\"UCI\"">>, [<<"private">>, {<<"community">>, <<"UCI">>}]},
  933. {<<"private=\"Content-Type, Content-Encoding, Content-Language\"">>,
  934. [{<<"private">>, [<<"content-type">>, <<"content-encoding">>, <<"content-language">>]}]}
  935. ],
  936. [{V, fun() -> R = parse_cache_control(V) end} || {V, R} <- Tests].
  937. parse_cache_control_error_test_() ->
  938. Tests = [
  939. <<>>
  940. ],
  941. [{V, fun() -> {'EXIT', _} = (catch parse_cache_control(V)) end} || V <- Tests].
  942. horse_parse_cache_control_no_cache() ->
  943. horse:repeat(200000,
  944. parse_cache_control(<<"no-cache">>)
  945. ).
  946. horse_parse_cache_control_max_age_0() ->
  947. horse:repeat(200000,
  948. parse_cache_control(<<"max-age=0">>)
  949. ).
  950. horse_parse_cache_control_max_age_30() ->
  951. horse:repeat(200000,
  952. parse_cache_control(<<"max-age=30">>)
  953. ).
  954. horse_parse_cache_control_custom() ->
  955. horse:repeat(200000,
  956. parse_cache_control(<<"private, community=\"UCI\"">>)
  957. ).
  958. horse_parse_cache_control_fields() ->
  959. horse:repeat(200000,
  960. parse_cache_control(<<"private=\"Content-Type, Content-Encoding, Content-Language\"">>)
  961. ).
  962. -endif.
  963. %% @doc Parse the Connection header.
  964. -spec parse_connection(binary()) -> [binary()].
  965. parse_connection(<<"close">>) ->
  966. [<<"close">>];
  967. parse_connection(<<"keep-alive">>) ->
  968. [<<"keep-alive">>];
  969. parse_connection(Connection) ->
  970. nonempty(token_ci_list(Connection, [])).
  971. -ifdef(TEST).
  972. prop_parse_connection() ->
  973. ?FORALL(L,
  974. non_empty(list(token())),
  975. begin
  976. << _, Connection/binary >> = iolist_to_binary([[$,, C] || C <- L]),
  977. ResL = parse_connection(Connection),
  978. CheckedL = [?LOWER(Co) =:= ResC || {Co, ResC} <- lists:zip(L, ResL)],
  979. [true] =:= lists:usort(CheckedL)
  980. end).
  981. parse_connection_test_() ->
  982. Tests = [
  983. {<<"close">>, [<<"close">>]},
  984. {<<"ClOsE">>, [<<"close">>]},
  985. {<<"Keep-Alive">>, [<<"keep-alive">>]},
  986. {<<"keep-alive, Upgrade">>, [<<"keep-alive">>, <<"upgrade">>]}
  987. ],
  988. [{V, fun() -> R = parse_connection(V) end} || {V, R} <- Tests].
  989. parse_connection_error_test_() ->
  990. Tests = [
  991. <<>>
  992. ],
  993. [{V, fun() -> {'EXIT', _} = (catch parse_connection(V)) end} || V <- Tests].
  994. horse_parse_connection_close() ->
  995. horse:repeat(200000,
  996. parse_connection(<<"close">>)
  997. ).
  998. horse_parse_connection_keepalive() ->
  999. horse:repeat(200000,
  1000. parse_connection(<<"keep-alive">>)
  1001. ).
  1002. horse_parse_connection_keepalive_upgrade() ->
  1003. horse:repeat(200000,
  1004. parse_connection(<<"keep-alive, upgrade">>)
  1005. ).
  1006. -endif.
  1007. %% @doc Parse the Content-Encoding header.
  1008. -spec parse_content_encoding(binary()) -> [binary()].
  1009. parse_content_encoding(ContentEncoding) ->
  1010. nonempty(token_ci_list(ContentEncoding, [])).
  1011. -ifdef(TEST).
  1012. parse_content_encoding_test_() ->
  1013. Tests = [
  1014. {<<"gzip">>, [<<"gzip">>]}
  1015. ],
  1016. [{V, fun() -> R = parse_content_encoding(V) end} || {V, R} <- Tests].
  1017. parse_content_encoding_error_test_() ->
  1018. Tests = [
  1019. <<>>
  1020. ],
  1021. [{V, fun() -> {'EXIT', _} = (catch parse_content_encoding(V)) end} || V <- Tests].
  1022. horse_parse_content_encoding() ->
  1023. horse:repeat(200000,
  1024. parse_content_encoding(<<"gzip">>)
  1025. ).
  1026. -endif.
  1027. %% @doc Parse the Content-Language header.
  1028. %%
  1029. %% We do not support irregular deprecated tags that do not match the ABNF.
  1030. -spec parse_content_language(binary()) -> [binary()].
  1031. parse_content_language(ContentLanguage) ->
  1032. nonempty(langtag_list(ContentLanguage, [])).
  1033. langtag_list(<<>>, Acc) -> lists:reverse(Acc);
  1034. langtag_list(<< C, R/bits >>, Acc) when ?IS_WS_COMMA(C) -> langtag_list(R, Acc);
  1035. langtag_list(<< A, B, C, R/bits >>, Acc) when ?IS_ALPHA(A), ?IS_ALPHA(B), ?IS_ALPHA(C) ->
  1036. langtag_extlang(R, Acc, << ?LC(A), ?LC(B), ?LC(C) >>, 0);
  1037. langtag_list(<< A, B, R/bits >>, Acc) when ?IS_ALPHA(A), ?IS_ALPHA(B) ->
  1038. langtag_extlang(R, Acc, << ?LC(A), ?LC(B) >>, 0);
  1039. langtag_list(<< X, R/bits >>, Acc) when X =:= $x; X =:= $X -> langtag_privateuse_sub(R, Acc, << $x >>, 0).
  1040. langtag_extlang(<< $-, A, B, C, D, E, F, G, H, R/bits >>, Acc, T, _)
  1041. when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D),
  1042. ?IS_ALPHANUM(E), ?IS_ALPHANUM(F), ?IS_ALPHANUM(G), ?IS_ALPHANUM(H) ->
  1043. langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F), ?LC(G), ?LC(H) >>);
  1044. langtag_extlang(<< $-, A, B, C, D, E, F, G, R/bits >>, Acc, T, _)
  1045. when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D),
  1046. ?IS_ALPHANUM(E), ?IS_ALPHANUM(F), ?IS_ALPHANUM(G) ->
  1047. langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F), ?LC(G) >>);
  1048. langtag_extlang(<< $-, A, B, C, D, E, F, R/bits >>, Acc, T, _)
  1049. when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D),
  1050. ?IS_ALPHANUM(E), ?IS_ALPHANUM(F) ->
  1051. langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F) >>);
  1052. langtag_extlang(<< $-, A, B, C, D, E, R/bits >>, Acc, T, _)
  1053. when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D), ?IS_ALPHANUM(E) ->
  1054. langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E) >>);
  1055. langtag_extlang(<< $-, A, B, C, D, R/bits >>, Acc, T, _)
  1056. when ?IS_ALPHA(A), ?IS_ALPHA(B), ?IS_ALPHA(C), ?IS_ALPHA(D) ->
  1057. langtag_region(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D) >>);
  1058. langtag_extlang(<< $-, A, B, C, R/bits >>, Acc, T, N)
  1059. when ?IS_ALPHA(A), ?IS_ALPHA(B), ?IS_ALPHA(C) ->
  1060. case N of
  1061. 2 -> langtag_script(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C) >>);
  1062. _ -> langtag_extlang(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C) >>, N + 1)
  1063. end;
  1064. langtag_extlang(R, Acc, T, _) -> langtag_region(R, Acc, T).
  1065. langtag_script(<< $-, A, B, C, D, E, F, G, H, R/bits >>, Acc, T)
  1066. when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D),
  1067. ?IS_ALPHANUM(E), ?IS_ALPHANUM(F), ?IS_ALPHANUM(G), ?IS_ALPHANUM(H) ->
  1068. langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F), ?LC(G), ?LC(H) >>);
  1069. langtag_script(<< $-, A, B, C, D, E, F, G, R/bits >>, Acc, T)
  1070. when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D),
  1071. ?IS_ALPHANUM(E), ?IS_ALPHANUM(F), ?IS_ALPHANUM(G) ->
  1072. langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F), ?LC(G) >>);
  1073. langtag_script(<< $-, A, B, C, D, E, F, R/bits >>, Acc, T)
  1074. when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D),
  1075. ?IS_ALPHANUM(E), ?IS_ALPHANUM(F) ->
  1076. langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F) >>);
  1077. langtag_script(<< $-, A, B, C, D, E, R/bits >>, Acc, T)
  1078. when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D), ?IS_ALPHANUM(E) ->
  1079. langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E) >>);
  1080. langtag_script(<< $-, A, B, C, D, R/bits >>, Acc, T)
  1081. when ?IS_ALPHA(A), ?IS_ALPHA(B), ?IS_ALPHA(C), ?IS_ALPHA(D) ->
  1082. langtag_region(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D) >>);
  1083. langtag_script(R, Acc, T) ->
  1084. langtag_region(R, Acc, T).
  1085. langtag_region(<< $-, A, B, C, D, E, F, G, H, R/bits >>, Acc, T)
  1086. when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D),
  1087. ?IS_ALPHANUM(E), ?IS_ALPHANUM(F), ?IS_ALPHANUM(G), ?IS_ALPHANUM(H) ->
  1088. langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F), ?LC(G), ?LC(H) >>);
  1089. langtag_region(<< $-, A, B, C, D, E, F, G, R/bits >>, Acc, T)
  1090. when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D),
  1091. ?IS_ALPHANUM(E), ?IS_ALPHANUM(F), ?IS_ALPHANUM(G) ->
  1092. langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F), ?LC(G) >>);
  1093. langtag_region(<< $-, A, B, C, D, E, F, R/bits >>, Acc, T)
  1094. when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D),
  1095. ?IS_ALPHANUM(E), ?IS_ALPHANUM(F) ->
  1096. langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F) >>);
  1097. langtag_region(<< $-, A, B, C, D, E, R/bits >>, Acc, T)
  1098. when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D), ?IS_ALPHANUM(E) ->
  1099. langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E) >>);
  1100. langtag_region(<< $-, A, B, C, D, R/bits >>, Acc, T)
  1101. when ?IS_DIGIT(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D) ->
  1102. langtag_variant(R, Acc, << T/binary, $-, A, ?LC(B), ?LC(C), ?LC(D) >>);
  1103. langtag_region(<< $-, A, B, R/bits >>, Acc, T) when ?IS_ALPHA(A), ?IS_ALPHA(B) ->
  1104. langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B) >>);
  1105. langtag_region(<< $-, A, B, C, R/bits >>, Acc, T) when ?IS_DIGIT(A), ?IS_DIGIT(B), ?IS_DIGIT(C) ->
  1106. langtag_variant(R, Acc, << T/binary, $-, A, B, C >>);
  1107. langtag_region(R, Acc, T) ->
  1108. langtag_variant(R, Acc, T).
  1109. langtag_variant(<< $-, A, B, C, D, E, F, G, H, R/bits >>, Acc, T)
  1110. when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D),
  1111. ?IS_ALPHANUM(E), ?IS_ALPHANUM(F), ?IS_ALPHANUM(G), ?IS_ALPHANUM(H) ->
  1112. langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F), ?LC(G), ?LC(H) >>);
  1113. langtag_variant(<< $-, A, B, C, D, E, F, G, R/bits >>, Acc, T)
  1114. when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D),
  1115. ?IS_ALPHANUM(E), ?IS_ALPHANUM(F), ?IS_ALPHANUM(G) ->
  1116. langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F), ?LC(G) >>);
  1117. langtag_variant(<< $-, A, B, C, D, E, F, R/bits >>, Acc, T)
  1118. when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D),
  1119. ?IS_ALPHANUM(E), ?IS_ALPHANUM(F) ->
  1120. langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F) >>);
  1121. langtag_variant(<< $-, A, B, C, D, E, R/bits >>, Acc, T)
  1122. when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D), ?IS_ALPHANUM(E) ->
  1123. langtag_variant(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E) >>);
  1124. langtag_variant(<< $-, A, B, C, D, R/bits >>, Acc, T)
  1125. when ?IS_DIGIT(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D) ->
  1126. langtag_variant(R, Acc, << T/binary, $-, A, ?LC(B), ?LC(C), ?LC(D) >>);
  1127. langtag_variant(R, Acc, T) ->
  1128. langtag_extension(R, Acc, T).
  1129. langtag_extension(<< $-, X, R/bits >>, Acc, T) when X =:= $x; X =:= $X -> langtag_privateuse_sub(R, Acc, << T/binary, $-, $x >>, 0);
  1130. langtag_extension(<< $-, S, R/bits >>, Acc, T) when ?IS_ALPHANUM(S) -> langtag_extension_sub(R, Acc, << T/binary, $-, ?LC(S) >>, 0);
  1131. langtag_extension(R, Acc, T) -> langtag_list_sep(R, [T|Acc]).
  1132. langtag_extension_sub(<< $-, A, B, C, D, E, F, G, H, R/bits >>, Acc, T, N)
  1133. when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D),
  1134. ?IS_ALPHANUM(E), ?IS_ALPHANUM(F), ?IS_ALPHANUM(G), ?IS_ALPHANUM(H) ->
  1135. langtag_extension_sub(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F), ?LC(G), ?LC(H) >>, N + 1);
  1136. langtag_extension_sub(<< $-, A, B, C, D, E, F, G, R/bits >>, Acc, T, N)
  1137. when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D),
  1138. ?IS_ALPHANUM(E), ?IS_ALPHANUM(F), ?IS_ALPHANUM(G) ->
  1139. langtag_extension_sub(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F), ?LC(G) >>, N + 1);
  1140. langtag_extension_sub(<< $-, A, B, C, D, E, F, R/bits >>, Acc, T, N)
  1141. when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D),
  1142. ?IS_ALPHANUM(E), ?IS_ALPHANUM(F) ->
  1143. langtag_extension_sub(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F) >>, N + 1);
  1144. langtag_extension_sub(<< $-, A, B, C, D, E, R/bits >>, Acc, T, N)
  1145. when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D), ?IS_ALPHANUM(E) ->
  1146. langtag_extension_sub(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E) >>, N + 1);
  1147. langtag_extension_sub(<< $-, A, B, C, D, R/bits >>, Acc, T, N)
  1148. when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D) ->
  1149. langtag_extension_sub(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D) >>, N + 1);
  1150. langtag_extension_sub(<< $-, A, B, C, R/bits >>, Acc, T, N)
  1151. when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C) ->
  1152. langtag_extension_sub(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C) >>, N + 1);
  1153. langtag_extension_sub(<< $-, A, B, R/bits >>, Acc, T, N)
  1154. when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B) ->
  1155. langtag_extension_sub(R, Acc, << T/binary, $-, ?LC(A), ?LC(B) >>, N + 1);
  1156. langtag_extension_sub(R, Acc, T, N) when N > 0 ->
  1157. langtag_extension(R, Acc, T).
  1158. langtag_privateuse_sub(<< $-, A, B, C, D, E, F, G, H, R/bits >>, Acc, T, N)
  1159. when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D),
  1160. ?IS_ALPHANUM(E), ?IS_ALPHANUM(F), ?IS_ALPHANUM(G), ?IS_ALPHANUM(H) ->
  1161. langtag_privateuse_sub(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F), ?LC(G), ?LC(H) >>, N + 1);
  1162. langtag_privateuse_sub(<< $-, A, B, C, D, E, F, G, R/bits >>, Acc, T, N)
  1163. when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D),
  1164. ?IS_ALPHANUM(E), ?IS_ALPHANUM(F), ?IS_ALPHANUM(G) ->
  1165. langtag_privateuse_sub(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F), ?LC(G) >>, N + 1);
  1166. langtag_privateuse_sub(<< $-, A, B, C, D, E, F, R/bits >>, Acc, T, N)
  1167. when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D),
  1168. ?IS_ALPHANUM(E), ?IS_ALPHANUM(F) ->
  1169. langtag_privateuse_sub(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E), ?LC(F) >>, N + 1);
  1170. langtag_privateuse_sub(<< $-, A, B, C, D, E, R/bits >>, Acc, T, N)
  1171. when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D), ?IS_ALPHANUM(E) ->
  1172. langtag_privateuse_sub(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D), ?LC(E) >>, N + 1);
  1173. langtag_privateuse_sub(<< $-, A, B, C, D, R/bits >>, Acc, T, N)
  1174. when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C), ?IS_ALPHANUM(D) ->
  1175. langtag_privateuse_sub(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C), ?LC(D) >>, N + 1);
  1176. langtag_privateuse_sub(<< $-, A, B, C, R/bits >>, Acc, T, N)
  1177. when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B), ?IS_ALPHANUM(C) ->
  1178. langtag_privateuse_sub(R, Acc, << T/binary, $-, ?LC(A), ?LC(B), ?LC(C) >>, N + 1);
  1179. langtag_privateuse_sub(<< $-, A, B, R/bits >>, Acc, T, N)
  1180. when ?IS_ALPHANUM(A), ?IS_ALPHANUM(B) ->
  1181. langtag_privateuse_sub(R, Acc, << T/binary, $-, ?LC(A), ?LC(B) >>, N + 1);
  1182. langtag_privateuse_sub(<< $-, A, R/bits >>, Acc, T, N)
  1183. when ?IS_ALPHANUM(A) ->
  1184. langtag_privateuse_sub(R, Acc, << T/binary, $-, ?LC(A) >>, N + 1);
  1185. langtag_privateuse_sub(R, Acc, T, N) when N > 0 -> langtag_list_sep(R, [T|Acc]).
  1186. langtag_list_sep(<<>>, Acc) -> lists:reverse(Acc);
  1187. langtag_list_sep(<< $,, R/bits >>, Acc) -> langtag_list(R, Acc);
  1188. langtag_list_sep(<< C, R/bits >>, Acc) when ?IS_WS(C) -> langtag_list_sep(R, Acc).
  1189. -ifdef(TEST).
  1190. langtag_language() -> vector(2, 3, alpha()).
  1191. langtag_extlang() -> vector(0, 3, [$-, alpha(), alpha(), alpha()]).
  1192. langtag_script() -> oneof([[], [$-, alpha(), alpha(), alpha(), alpha()]]).
  1193. langtag_region() -> oneof([[], [$-, alpha(), alpha()], [$-, digit(), digit(), digit()]]).
  1194. langtag_variant() ->
  1195. small_list(frequency([
  1196. {4, [$-, vector(5, 8, alphanum())]},
  1197. {1, [$-, digit(), alphanum(), alphanum(), alphanum()]}
  1198. ])).
  1199. langtag_extension() ->
  1200. small_list([$-, ?SUCHTHAT(S, alphanum(), S =/= $x andalso S =/= $X),
  1201. small_non_empty_list([$-, vector(2, 8, alphanum())])
  1202. ]).
  1203. langtag_privateuse() -> oneof([[], [$-, langtag_privateuse_nodash()]]).
  1204. langtag_privateuse_nodash() -> [elements([$x, $X]), small_non_empty_list([$-, vector(1, 8, alphanum())])].
  1205. private_language_tag() -> ?LET(T, langtag_privateuse_nodash(), iolist_to_binary(T)).
  1206. language_tag() ->
  1207. ?LET(IoList,
  1208. [langtag_language(), langtag_extlang(), langtag_script(), langtag_region(),
  1209. langtag_variant(), langtag_extension(), langtag_privateuse()],
  1210. iolist_to_binary(IoList)).
  1211. content_language() ->
  1212. ?LET(L,
  1213. non_empty(list(frequency([
  1214. {90, language_tag()},
  1215. {10, private_language_tag()}
  1216. ]))),
  1217. begin
  1218. << _, ContentLanguage/binary >> = iolist_to_binary([[$,, T] || T <- L]),
  1219. {L, ContentLanguage}
  1220. end).
  1221. prop_parse_content_language() ->
  1222. ?FORALL({L, ContentLanguage},
  1223. content_language(),
  1224. begin
  1225. ResL = parse_content_language(ContentLanguage),
  1226. CheckedL = [?LOWER(T) =:= ResT || {T, ResT} <- lists:zip(L, ResL)],
  1227. [true] =:= lists:usort(CheckedL)
  1228. end).
  1229. parse_content_language_test_() ->
  1230. Tests = [
  1231. {<<"de">>, [<<"de">>]},
  1232. {<<"fr">>, [<<"fr">>]},
  1233. {<<"ja">>, [<<"ja">>]},
  1234. {<<"zh-Hant">>, [<<"zh-hant">>]},
  1235. {<<"zh-Hans">>, [<<"zh-hans">>]},
  1236. {<<"sr-Cyrl">>, [<<"sr-cyrl">>]},
  1237. {<<"sr-Latn">>, [<<"sr-latn">>]},
  1238. {<<"zh-cmn-Hans-CN">>, [<<"zh-cmn-hans-cn">>]},
  1239. {<<"cmn-Hans-CN">>, [<<"cmn-hans-cn">>]},
  1240. {<<"zh-yue-HK">>, [<<"zh-yue-hk">>]},
  1241. {<<"yue-HK">>, [<<"yue-hk">>]},
  1242. {<<"zh-Hans-CN">>, [<<"zh-hans-cn">>]},
  1243. {<<"sr-Latn-RS">>, [<<"sr-latn-rs">>]},
  1244. {<<"sl-rozaj">>, [<<"sl-rozaj">>]},
  1245. {<<"sl-rozaj-biske">>, [<<"sl-rozaj-biske">>]},
  1246. {<<"sl-nedis">>, [<<"sl-nedis">>]},
  1247. {<<"de-CH-1901">>, [<<"de-ch-1901">>]},
  1248. {<<"sl-IT-nedis">>, [<<"sl-it-nedis">>]},
  1249. {<<"hy-Latn-IT-arevela">>, [<<"hy-latn-it-arevela">>]},
  1250. {<<"de-DE">>, [<<"de-de">>]},
  1251. {<<"en-US">>, [<<"en-us">>]},
  1252. {<<"es-419">>, [<<"es-419">>]},
  1253. {<<"de-CH-x-phonebk">>, [<<"de-ch-x-phonebk">>]},
  1254. {<<"az-Arab-x-AZE-derbend">>, [<<"az-arab-x-aze-derbend">>]},
  1255. {<<"x-whatever">>, [<<"x-whatever">>]},
  1256. {<<"qaa-Qaaa-QM-x-southern">>, [<<"qaa-qaaa-qm-x-southern">>]},
  1257. {<<"de-Qaaa">>, [<<"de-qaaa">>]},
  1258. {<<"sr-Latn-QM">>, [<<"sr-latn-qm">>]},
  1259. {<<"sr-Qaaa-RS">>, [<<"sr-qaaa-rs">>]},
  1260. {<<"en-US-u-islamcal">>, [<<"en-us-u-islamcal">>]},
  1261. {<<"zh-CN-a-myext-x-private">>, [<<"zh-cn-a-myext-x-private">>]},
  1262. {<<"en-a-myext-b-another">>, [<<"en-a-myext-b-another">>]},
  1263. {<<"mn-Cyrl-MN">>, [<<"mn-cyrl-mn">>]},
  1264. {<<"MN-cYRL-mn">>, [<<"mn-cyrl-mn">>]},
  1265. {<<"mN-cYrL-Mn">>, [<<"mn-cyrl-mn">>]},
  1266. {<<"az-Arab-IR">>, [<<"az-arab-ir">>]},
  1267. {<<"zh-gan">>, [<<"zh-gan">>]},
  1268. {<<"zh-yue">>, [<<"zh-yue">>]},
  1269. {<<"zh-cmn">>, [<<"zh-cmn">>]},
  1270. {<<"de-AT">>, [<<"de-at">>]},
  1271. {<<"de-CH-1996">>, [<<"de-ch-1996">>]},
  1272. {<<"en-Latn-GB-boont-r-extended-sequence-x-private">>,
  1273. [<<"en-latn-gb-boont-r-extended-sequence-x-private">>]},
  1274. {<<"el-x-koine">>, [<<"el-x-koine">>]},
  1275. {<<"el-x-attic">>, [<<"el-x-attic">>]},
  1276. {<<"fr, en-US, es-419, az-Arab, x-pig-latin, man-Nkoo-GN">>,
  1277. [<<"fr">>, <<"en-us">>, <<"es-419">>, <<"az-arab">>, <<"x-pig-latin">>, <<"man-nkoo-gn">>]},
  1278. {<<"da">>, [<<"da">>]},
  1279. {<<"mi, en">>, [<<"mi">>, <<"en">>]}
  1280. ],
  1281. [{V, fun() -> R = parse_content_language(V) end} || {V, R} <- Tests].
  1282. parse_content_language_error_test_() ->
  1283. Tests = [
  1284. <<>>
  1285. ],
  1286. [{V, fun() -> {'EXIT', _} = (catch parse_content_language(V)) end} || V <- Tests].
  1287. horse_parse_content_language() ->
  1288. horse:repeat(100000,
  1289. parse_content_language(<<"fr, en-US, es-419, az-Arab, x-pig-latin, man-Nkoo-GN">>)
  1290. ).
  1291. -endif.
  1292. %% @doc Parse the Content-Length header.
  1293. -spec parse_content_length(binary()) -> non_neg_integer().
  1294. parse_content_length(ContentLength) ->
  1295. I = binary_to_integer(ContentLength),
  1296. true = I >= 0,
  1297. I.
  1298. -ifdef(TEST).
  1299. prop_parse_content_length() ->
  1300. ?FORALL(
  1301. X,
  1302. non_neg_integer(),
  1303. X =:= parse_content_length(integer_to_binary(X))
  1304. ).
  1305. parse_content_length_test_() ->
  1306. Tests = [
  1307. {<<"0">>, 0},
  1308. {<<"42">>, 42},
  1309. {<<"69">>, 69},
  1310. {<<"1337">>, 1337},
  1311. {<<"3495">>, 3495},
  1312. {<<"1234567890">>, 1234567890}
  1313. ],
  1314. [{V, fun() -> R = parse_content_length(V) end} || {V, R} <- Tests].
  1315. parse_content_length_error_test_() ->
  1316. Tests = [
  1317. <<>>,
  1318. <<"-1">>,
  1319. <<"123, 123">>,
  1320. <<"4.17">>
  1321. ],
  1322. [{V, fun() -> {'EXIT', _} = (catch parse_content_length(V)) end} || V <- Tests].
  1323. horse_parse_content_length_zero() ->
  1324. horse:repeat(100000,
  1325. parse_content_length(<<"0">>)
  1326. ).
  1327. horse_parse_content_length_giga() ->
  1328. horse:repeat(100000,
  1329. parse_content_length(<<"1234567890">>)
  1330. ).
  1331. -endif.
  1332. %% @doc Parse the Content-Range header.
  1333. -spec parse_content_range(binary())
  1334. -> {bytes, non_neg_integer(), non_neg_integer(), non_neg_integer() | '*'}
  1335. | {bytes, '*', non_neg_integer()} | {binary(), binary()}.
  1336. parse_content_range(<<"bytes */", C, R/bits >>) when ?IS_DIGIT(C) -> unsatisfied_range(R, C - $0);
  1337. parse_content_range(<<"bytes ", C, R/bits >>) when ?IS_DIGIT(C) -> byte_range_first(R, C - $0);
  1338. parse_content_range(<< C, R/bits >>) when ?IS_TOKEN(C) ->
  1339. ?LOWER(other_content_range_unit, R, <<>>).
  1340. byte_range_first(<< $-, C, R/bits >>, First) when ?IS_DIGIT(C) -> byte_range_last(R, First, C - $0);
  1341. byte_range_first(<< C, R/bits >>, First) when ?IS_DIGIT(C) -> byte_range_first(R, First * 10 + C - $0).
  1342. byte_range_last(<<"/*">>, First, Last) -> {bytes, First, Last, '*'};
  1343. byte_range_last(<< $/, C, R/bits >>, First, Last) when ?IS_DIGIT(C) -> byte_range_complete(R, First, Last, C - $0);
  1344. byte_range_last(<< C, R/bits >>, First, Last) when ?IS_DIGIT(C) -> byte_range_last(R, First, Last * 10 + C - $0).
  1345. byte_range_complete(<<>>, First, Last, Complete) -> {bytes, First, Last, Complete};
  1346. byte_range_complete(<< C, R/bits >>, First, Last, Complete) when ?IS_DIGIT(C) ->
  1347. byte_range_complete(R, First, Last, Complete * 10 + C - $0).
  1348. unsatisfied_range(<<>>, Complete) -> {bytes, '*', Complete};
  1349. unsatisfied_range(<< C, R/bits >>, Complete) when ?IS_DIGIT(C) -> unsatisfied_range(R, Complete * 10 + C - $0).
  1350. other_content_range_unit(<< $\s, R/bits >>, Unit) -> other_content_range_resp(R, Unit, <<>>);
  1351. other_content_range_unit(<< C, R/bits >>, Unit) when ?IS_TOKEN(C) ->
  1352. ?LOWER(other_content_range_unit, R, Unit).
  1353. other_content_range_resp(<<>>, Unit, Resp) -> {Unit, Resp};
  1354. other_content_range_resp(<< C, R/bits >>, Unit, Resp) when ?IS_CHAR(C) -> other_content_range_resp(R, Unit, << Resp/binary, C >>).
  1355. -ifdef(TEST).
  1356. content_range() ->
  1357. ?LET(ContentRange,
  1358. oneof([
  1359. ?SUCHTHAT({bytes, First, Last, Complete},
  1360. {bytes, non_neg_integer(), non_neg_integer(), non_neg_integer()},
  1361. First =< Last andalso Last < Complete),
  1362. ?SUCHTHAT({bytes, First, Last, '*'},
  1363. {bytes, non_neg_integer(), non_neg_integer(), '*'},
  1364. First =< Last),
  1365. {bytes, '*', non_neg_integer()},
  1366. {token(), ?LET(L, list(abnf_char()), list_to_binary(L))}
  1367. ]),
  1368. {case ContentRange of
  1369. {Unit, Resp} when is_binary(Unit) -> {?LOWER(Unit), Resp};
  1370. _ -> ContentRange
  1371. end, case ContentRange of
  1372. {bytes, First, Last, '*'} ->
  1373. << "bytes ", (integer_to_binary(First))/binary, "-",
  1374. (integer_to_binary(Last))/binary, "/*">>;
  1375. {bytes, First, Last, Complete} ->
  1376. << "bytes ", (integer_to_binary(First))/binary, "-",
  1377. (integer_to_binary(Last))/binary, "/", (integer_to_binary(Complete))/binary >>;
  1378. {bytes, '*', Complete} ->
  1379. << "bytes */", (integer_to_binary(Complete))/binary >>;
  1380. {Unit, Resp} ->
  1381. << Unit/binary, $\s, Resp/binary >>
  1382. end}).
  1383. prop_parse_content_range() ->
  1384. ?FORALL({Res, ContentRange},
  1385. content_range(),
  1386. Res =:= parse_content_range(ContentRange)).
  1387. parse_content_range_test_() ->
  1388. Tests = [
  1389. {<<"bytes 21010-47021/47022">>, {bytes, 21010, 47021, 47022}},
  1390. {<<"bytes 500-999/8000">>, {bytes, 500, 999, 8000}},
  1391. {<<"bytes 7000-7999/8000">>, {bytes, 7000, 7999, 8000}},
  1392. {<<"bytes 42-1233/1234">>, {bytes, 42, 1233, 1234}},
  1393. {<<"bytes 42-1233/*">>, {bytes, 42, 1233, '*'}},
  1394. {<<"bytes */1234">>, {bytes, '*', 1234}},
  1395. {<<"bytes 0-499/1234">>, {bytes, 0, 499, 1234}},
  1396. {<<"bytes 500-999/1234">>, {bytes, 500, 999, 1234}},
  1397. {<<"bytes 500-1233/1234">>, {bytes, 500, 1233, 1234}},
  1398. {<<"bytes 734-1233/1234">>, {bytes, 734, 1233, 1234}},
  1399. {<<"bytes */47022">>, {bytes, '*', 47022}},
  1400. {<<"exampleunit 1.2-4.3/25">>, {<<"exampleunit">>, <<"1.2-4.3/25">>}},
  1401. {<<"exampleunit 11.2-14.3/25">>, {<<"exampleunit">>, <<"11.2-14.3/25">>}}
  1402. ],
  1403. [{V, fun() -> R = parse_content_range(V) end} || {V, R} <- Tests].
  1404. parse_content_range_error_test_() ->
  1405. Tests = [
  1406. <<>>
  1407. ],
  1408. [{V, fun() -> {'EXIT', _} = (catch parse_content_range(V)) end} || V <- Tests].
  1409. horse_parse_content_range_bytes() ->
  1410. horse:repeat(200000,
  1411. parse_content_range(<<"bytes 21010-47021/47022">>)
  1412. ).
  1413. horse_parse_content_range_other() ->
  1414. horse:repeat(200000,
  1415. parse_content_range(<<"exampleunit 11.2-14.3/25">>)
  1416. ).
  1417. -endif.
  1418. %% @doc Parse the Content-Type header.
  1419. -spec parse_content_type(binary()) -> media_type().
  1420. parse_content_type(<< C, R/bits >>) when ?IS_TOKEN(C) ->
  1421. ?LOWER(media_type, R, <<>>).
  1422. media_type(<< $/, C, R/bits >>, T) when ?IS_TOKEN(C) ->
  1423. ?LOWER(media_subtype, R, T, <<>>);
  1424. media_type(<< C, R/bits >>, T) when ?IS_TOKEN(C) ->
  1425. ?LOWER(media_type, R, T).
  1426. media_subtype(<< C, R/bits >>, T, S) when ?IS_TOKEN(C) ->
  1427. ?LOWER(media_subtype, R, T, S);
  1428. media_subtype(R, T, S) -> media_param_sep(R, T, S, []).
  1429. media_param_sep(<<>>, T, S, P) -> {T, S, lists:reverse(P)};
  1430. media_param_sep(<< $;, R/bits >>, T, S, P) -> media_before_param(R, T, S, P);
  1431. media_param_sep(<< C, R/bits >>, T, S, P) when ?IS_WS(C) -> media_param_sep(R, T, S, P).
  1432. media_before_param(<< C, R/bits >>, T, S, P) when ?IS_WS(C)-> media_before_param(R, T, S, P);
  1433. media_before_param(<< "charset=", $", R/bits >>, T, S, P) -> media_charset_quoted(R, T, S, P, <<>>);
  1434. media_before_param(<< "charset=", R/bits >>, T, S, P) -> media_charset(R, T, S, P, <<>>);
  1435. media_before_param(<< C, R/bits >>, T, S, P) when ?IS_TOKEN(C) ->
  1436. ?LOWER(media_param, R, T, S, P, <<>>).
  1437. media_charset_quoted(<< $", R/bits >>, T, S, P, V) ->
  1438. media_param_sep(R, T, S, [{<<"charset">>, V}|P]);
  1439. media_charset_quoted(<< $\\, C, R/bits >>, T, S, P, V) when ?IS_VCHAR_OBS(C) ->
  1440. ?LOWER(media_charset_quoted, R, T, S, P, V);
  1441. media_charset_quoted(<< C, R/bits >>, T, S, P, V) when ?IS_VCHAR_OBS(C) ->
  1442. ?LOWER(media_charset_quoted, R, T, S, P, V).
  1443. media_charset(<< C, R/bits >>, T, S, P, V) when ?IS_TOKEN(C) ->
  1444. ?LOWER(media_charset, R, T, S, P, V);
  1445. media_charset(R, T, S, P, V) -> media_param_sep(R, T, S, [{<<"charset">>, V}|P]).
  1446. media_param(<< $=, $", R/bits >>, T, S, P, K) -> media_quoted(R, T, S, P, K, <<>>);
  1447. media_param(<< $=, C, R/bits >>, T, S, P, K) when ?IS_TOKEN(C) -> media_value(R, T, S, P, K, << C >>);
  1448. media_param(<< C, R/bits >>, T, S, P, K) when ?IS_TOKEN(C) ->
  1449. ?LOWER(media_param, R, T, S, P, K).
  1450. media_quoted(<< $", R/bits >>, T, S, P, K, V) -> media_param_sep(R, T, S, [{K, V}|P]);
  1451. media_quoted(<< $\\, C, R/bits >>, T, S, P, K, V) when ?IS_VCHAR_OBS(C) -> media_quoted(R, T, S, P, K, << V/binary, C >>);
  1452. media_quoted(<< C, R/bits >>, T, S, P, K, V) when ?IS_VCHAR_OBS(C) -> media_quoted(R, T, S, P, K, << V/binary, C >>).
  1453. media_value(<< C, R/bits >>, T, S, P, K, V) when ?IS_TOKEN(C) -> media_value(R, T, S, P, K, << V/binary, C >>);
  1454. media_value(R, T, S, P, K, V) -> media_param_sep(R, T, S, [{K, V}|P]).
  1455. -ifdef(TEST).
  1456. media_type_parameter() ->
  1457. frequency([
  1458. {90, parameter()},
  1459. {10, {<<"charset">>, oneof([token(), quoted_string()]), <<>>, <<>>}}
  1460. ]).
  1461. media_type() ->
  1462. ?LET({T, S, P},
  1463. {token(), token(), small_list(media_type_parameter())},
  1464. {T, S, P, iolist_to_binary([T, $/, S, [[OWS1, $;, OWS2, K, $=, V] || {K, V, OWS1, OWS2} <- P]])}
  1465. ).
  1466. prop_parse_content_type() ->
  1467. ?FORALL({T, S, P, MediaType},
  1468. media_type(),
  1469. begin
  1470. {ResT, ResS, ResP} = parse_content_type(MediaType),
  1471. ExpectedP = [case ?LOWER(K) of
  1472. <<"charset">> -> {<<"charset">>, ?LOWER(unquote(V))};
  1473. LowK -> {LowK, unquote(V)}
  1474. end || {K, V, _, _} <- P],
  1475. ResT =:= ?LOWER(T)
  1476. andalso ResS =:= ?LOWER(S)
  1477. andalso ResP =:= ExpectedP
  1478. end
  1479. ).
  1480. parse_content_type_test_() ->
  1481. Tests = [
  1482. {<<"text/html;charset=utf-8">>,
  1483. {<<"text">>, <<"html">>, [{<<"charset">>, <<"utf-8">>}]}},
  1484. {<<"text/html;charset=UTF-8">>,
  1485. {<<"text">>, <<"html">>, [{<<"charset">>, <<"utf-8">>}]}},
  1486. {<<"Text/HTML;Charset=\"utf-8\"">>,
  1487. {<<"text">>, <<"html">>, [{<<"charset">>, <<"utf-8">>}]}},
  1488. {<<"text/html; charset=\"utf-8\"">>,
  1489. {<<"text">>, <<"html">>, [{<<"charset">>, <<"utf-8">>}]}},
  1490. {<<"text/html; charset=ISO-8859-4">>,
  1491. {<<"text">>, <<"html">>, [{<<"charset">>, <<"iso-8859-4">>}]}},
  1492. {<<"text/plain; charset=iso-8859-4">>,
  1493. {<<"text">>, <<"plain">>, [{<<"charset">>, <<"iso-8859-4">>}]}},
  1494. {<<"multipart/form-data \t;Boundary=\"MultipartIsUgly\"">>,
  1495. {<<"multipart">>, <<"form-data">>, [
  1496. {<<"boundary">>, <<"MultipartIsUgly">>}
  1497. ]}},
  1498. {<<"foo/bar; one=FirstParam; two=SecondParam">>,
  1499. {<<"foo">>, <<"bar">>, [
  1500. {<<"one">>, <<"FirstParam">>},
  1501. {<<"two">>, <<"SecondParam">>}
  1502. ]}}
  1503. ],
  1504. [{V, fun() -> R = parse_content_type(V) end} || {V, R} <- Tests].
  1505. horse_parse_content_type() ->
  1506. horse:repeat(200000,
  1507. parse_content_type(<<"text/html;charset=utf-8">>)
  1508. ).
  1509. -endif.
  1510. %% @doc Parse the Date header.
  1511. -spec parse_date(binary()) -> calendar:datetime().
  1512. parse_date(Date) ->
  1513. cow_date:parse_date(Date).
  1514. -ifdef(TEST).
  1515. parse_date_test_() ->
  1516. Tests = [
  1517. {<<"Tue, 15 Nov 1994 08:12:31 GMT">>, {{1994, 11, 15}, {8, 12, 31}}}
  1518. ],
  1519. [{V, fun() -> R = parse_date(V) end} || {V, R} <- Tests].
  1520. -endif.
  1521. %% @doc Parse the ETag header.
  1522. -spec parse_etag(binary()) -> etag().
  1523. parse_etag(<< $W, $/, $", R/bits >>) ->
  1524. etag(R, weak, <<>>);
  1525. parse_etag(<< $", R/bits >>) ->
  1526. etag(R, strong, <<>>).
  1527. etag(<< $" >>, Strength, Tag) ->
  1528. {Strength, Tag};
  1529. etag(<< C, R/bits >>, Strength, Tag) when ?IS_ETAGC(C) ->
  1530. etag(R, Strength, << Tag/binary, C >>).
  1531. -ifdef(TEST).
  1532. etagc() ->
  1533. ?SUCHTHAT(C, int(16#21, 16#ff), C =/= 16#22 andalso C =/= 16#7f).
  1534. etag() ->
  1535. ?LET({Strength, Tag},
  1536. {elements([weak, strong]), list(etagc())},
  1537. begin
  1538. TagBin = list_to_binary(Tag),
  1539. {{Strength, TagBin},
  1540. case Strength of
  1541. weak -> << $W, $/, $", TagBin/binary, $" >>;
  1542. strong -> << $", TagBin/binary, $" >>
  1543. end}
  1544. end).
  1545. prop_parse_etag() ->
  1546. ?FORALL({Tag, TagBin},
  1547. etag(),
  1548. Tag =:= parse_etag(TagBin)).
  1549. parse_etag_test_() ->
  1550. Tests = [
  1551. {<<"\"xyzzy\"">>, {strong, <<"xyzzy">>}},
  1552. {<<"W/\"xyzzy\"">>, {weak, <<"xyzzy">>}},
  1553. {<<"\"\"">>, {strong, <<>>}}
  1554. ],
  1555. [{V, fun() -> R = parse_etag(V) end} || {V, R} <- Tests].
  1556. parse_etag_error_test_() ->
  1557. Tests = [
  1558. <<>>,
  1559. <<"\"">>,
  1560. <<"W">>,
  1561. <<"W/">>
  1562. ],
  1563. [{V, fun() -> {'EXIT', _} = (catch parse_etag(V)) end} || V <- Tests].
  1564. horse_parse_etag() ->
  1565. horse:repeat(200000,
  1566. parse_etag(<<"W/\"xyzzy\"">>)
  1567. ).
  1568. -endif.
  1569. %% @doc Parse the Expect header.
  1570. -spec parse_expect(binary()) -> continue.
  1571. parse_expect(<<"100-continue">>) ->
  1572. continue;
  1573. parse_expect(<<"100-", C, O, N, T, I, M, U, E >>)
  1574. when (C =:= $C) or (C =:= $c), (O =:= $O) or (O =:= $o),
  1575. (N =:= $N) or (N =:= $n), (T =:= $T) or (T =:= $t),
  1576. (I =:= $I) or (I =:= $i), (M =:= $N) or (M =:= $n),
  1577. (U =:= $U) or (U =:= $u), (E =:= $E) or (E =:= $e) ->
  1578. continue.
  1579. -ifdef(TEST).
  1580. expect() ->
  1581. ?LET(E,
  1582. [$1, $0, $0, $-,
  1583. elements([$c, $C]), elements([$o, $O]), elements([$n, $N]),
  1584. elements([$t, $T]), elements([$i, $I]), elements([$n, $N]),
  1585. elements([$u, $U]), elements([$e, $E])],
  1586. list_to_binary(E)).
  1587. prop_parse_expect() ->
  1588. ?FORALL(E, expect(), continue =:= parse_expect(E)).
  1589. parse_expect_test_() ->
  1590. Tests = [
  1591. <<"100-continue">>,
  1592. <<"100-CONTINUE">>,
  1593. <<"100-Continue">>,
  1594. <<"100-CoNtInUe">>
  1595. ],
  1596. [{V, fun() -> continue = parse_expect(V) end} || V <- Tests].
  1597. parse_expect_error_test_() ->
  1598. Tests = [
  1599. <<>>,
  1600. <<" ">>,
  1601. <<"200-OK">>,
  1602. <<"Cookies">>
  1603. ],
  1604. [{V, fun() -> {'EXIT', _} = (catch parse_expect(V)) end} || V <- Tests].
  1605. horse_parse_expect() ->
  1606. horse:repeat(200000,
  1607. parse_expect(<<"100-continue">>)
  1608. ).
  1609. -endif.
  1610. %% @doc Parse the Expires header.
  1611. %%
  1612. %% Recipients must interpret invalid date formats as a date
  1613. %% in the past. The value "0" is commonly used.
  1614. -spec parse_expires(binary()) -> calendar:datetime().
  1615. parse_expires(<<"0">>) ->
  1616. {{1, 1, 1}, {0, 0, 0}};
  1617. parse_expires(Expires) ->
  1618. try
  1619. cow_date:parse_date(Expires)
  1620. catch _:_ ->
  1621. {{1, 1, 1}, {0, 0, 0}}
  1622. end.
  1623. -ifdef(TEST).
  1624. parse_expires_test_() ->
  1625. Tests = [
  1626. {<<"0">>, {{1, 1, 1}, {0, 0, 0}}},
  1627. {<<"Thu, 01 Dec 1994 nope invalid">>, {{1, 1, 1}, {0, 0, 0}}},
  1628. {<<"Thu, 01 Dec 1994 16:00:00 GMT">>, {{1994, 12, 1}, {16, 0, 0}}}
  1629. ],
  1630. [{V, fun() -> R = parse_expires(V) end} || {V, R} <- Tests].
  1631. horse_parse_expires_0() ->
  1632. horse:repeat(200000,
  1633. parse_expires(<<"0">>)
  1634. ).
  1635. horse_parse_expires_invalid() ->
  1636. horse:repeat(200000,
  1637. parse_expires(<<"Thu, 01 Dec 1994 nope invalid">>)
  1638. ).
  1639. -endif.
  1640. %% @doc Parse the Host header.
  1641. %%
  1642. %% We only seek to have legal characters and separate the
  1643. %% host and port values. The number of segments in the host
  1644. %% or the size of each segment is not checked.
  1645. %%
  1646. %% There is no way to distinguish IPv4 addresses from regular
  1647. %% names until the last segment is reached therefore we do not
  1648. %% differentiate them.
  1649. %%
  1650. %% The following valid hosts are currently rejected: IPv6
  1651. %% addresses with a zone identifier; IPvFuture addresses;
  1652. %% and percent-encoded addresses.
  1653. -spec parse_host(binary()) -> {binary(), 0..65535 | undefined}.
  1654. parse_host(<< $[, R/bits >>) ->
  1655. ipv6_address(R, << $[ >>);
  1656. parse_host(Host) ->
  1657. reg_name(Host, <<>>).
  1658. ipv6_address(<< $] >>, IP) -> {<< IP/binary, $] >>, undefined};
  1659. ipv6_address(<< $], $:, Port/bits >>, IP) -> {<< IP/binary, $] >>, binary_to_integer(Port)};
  1660. ipv6_address(<< C, R/bits >>, IP) when ?IS_HEX(C) or (C =:= $:) or (C =:= $.) ->
  1661. ?LOWER(ipv6_address, R, IP).
  1662. reg_name(<<>>, Name) -> {Name, undefined};
  1663. reg_name(<< $:, Port/bits >>, Name) -> {Name, binary_to_integer(Port)};
  1664. reg_name(<< C, R/bits >>, Name) when ?IS_URI_UNRESERVED(C) or ?IS_URI_SUB_DELIMS(C) ->
  1665. ?LOWER(reg_name, R, Name).
  1666. -ifdef(TEST).
  1667. host_chars() -> "!$&'()*+,-.0123456789;=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~".
  1668. host() -> vector(1, 255, elements(host_chars())).
  1669. host_port() ->
  1670. ?LET({Host, Port},
  1671. {host(), oneof([undefined, int(1, 65535)])},
  1672. begin
  1673. HostBin = list_to_binary(Host),
  1674. {{?LOWER(HostBin), Port},
  1675. case Port of
  1676. undefined -> HostBin;
  1677. _ -> << HostBin/binary, $:, (integer_to_binary(Port))/binary >>
  1678. end}
  1679. end).
  1680. prop_parse_host() ->
  1681. ?FORALL({Res, Host}, host_port(), Res =:= parse_host(Host)).
  1682. parse_host_test_() ->
  1683. Tests = [
  1684. {<<>>, {<<>>, undefined}},
  1685. {<<"www.example.org:8080">>, {<<"www.example.org">>, 8080}},
  1686. {<<"www.example.org">>, {<<"www.example.org">>, undefined}},
  1687. {<<"192.0.2.1:8080">>, {<<"192.0.2.1">>, 8080}},
  1688. {<<"192.0.2.1">>, {<<"192.0.2.1">>, undefined}},
  1689. {<<"[2001:db8::1]:8080">>, {<<"[2001:db8::1]">>, 8080}},
  1690. {<<"[2001:db8::1]">>, {<<"[2001:db8::1]">>, undefined}},
  1691. {<<"[::ffff:192.0.2.1]:8080">>, {<<"[::ffff:192.0.2.1]">>, 8080}},
  1692. {<<"[::ffff:192.0.2.1]">>, {<<"[::ffff:192.0.2.1]">>, undefined}}
  1693. ],
  1694. [{V, fun() -> R = parse_host(V) end} || {V, R} <- Tests].
  1695. horse_parse_host_blue_example_org() ->
  1696. horse:repeat(200000,
  1697. parse_host(<<"blue.example.org:8080">>)
  1698. ).
  1699. horse_parse_host_ipv4() ->
  1700. horse:repeat(200000,
  1701. parse_host(<<"192.0.2.1:8080">>)
  1702. ).
  1703. horse_parse_host_ipv6() ->
  1704. horse:repeat(200000,
  1705. parse_host(<<"[2001:db8::1]:8080">>)
  1706. ).
  1707. horse_parse_host_ipv6_v4() ->
  1708. horse:repeat(200000,
  1709. parse_host(<<"[::ffff:192.0.2.1]:8080">>)
  1710. ).
  1711. -endif.
  1712. %% @doc Parse the HTTP2-Settings header.
  1713. -spec parse_http2_settings(binary()) -> binary().
  1714. parse_http2_settings(HTTP2Settings) ->
  1715. cow_http2:parse_settings_payload(base64:decode(HTTP2Settings)).
  1716. %% @doc Parse the If-Match header.
  1717. -spec parse_if_match(binary()) -> '*' | [etag()].
  1718. parse_if_match(<<"*">>) ->
  1719. '*';
  1720. parse_if_match(IfMatch) ->
  1721. nonempty(etag_list(IfMatch, [])).
  1722. etag_list(<<>>, Acc) -> lists:reverse(Acc);
  1723. etag_list(<< C, R/bits >>, Acc) when ?IS_WS_COMMA(C) -> etag_list(R, Acc);
  1724. etag_list(<< $W, $/, $", R/bits >>, Acc) -> etag(R, Acc, weak, <<>>);
  1725. etag_list(<< $", R/bits >>, Acc) -> etag(R, Acc, strong, <<>>).
  1726. etag(<< $", R/bits >>, Acc, Strength, Tag) -> etag_list_sep(R, [{Strength, Tag}|Acc]);
  1727. etag(<< C, R/bits >>, Acc, Strength, Tag) when ?IS_ETAGC(C) -> etag(R, Acc, Strength, << Tag/binary, C >>).
  1728. etag_list_sep(<<>>, Acc) -> lists:reverse(Acc);
  1729. etag_list_sep(<< C, R/bits >>, Acc) when ?IS_WS(C) -> etag_list_sep(R, Acc);
  1730. etag_list_sep(<< $,, R/bits >>, Acc) -> etag_list(R, Acc).
  1731. -ifdef(TEST).
  1732. prop_parse_if_match() ->
  1733. ?FORALL(L,
  1734. non_empty(list(etag())),
  1735. begin
  1736. << _, IfMatch/binary >> = iolist_to_binary([[$,, T] || {_, T} <- L]),
  1737. ResL = parse_if_match(IfMatch),
  1738. CheckedL = [T =:= ResT || {{T, _}, ResT} <- lists:zip(L, ResL)],
  1739. [true] =:= lists:usort(CheckedL)
  1740. end).
  1741. parse_if_match_test_() ->
  1742. Tests = [
  1743. {<<"\"xyzzy\"">>, [{strong, <<"xyzzy">>}]},
  1744. {<<"\"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\"">>,
  1745. [{strong, <<"xyzzy">>}, {strong, <<"r2d2xxxx">>}, {strong, <<"c3piozzzz">>}]},
  1746. {<<"*">>, '*'}
  1747. ],
  1748. [{V, fun() -> R = parse_if_match(V) end} || {V, R} <- Tests].
  1749. parse_if_match_error_test_() ->
  1750. Tests = [
  1751. <<>>
  1752. ],
  1753. [{V, fun() -> {'EXIT', _} = (catch parse_if_match(V)) end} || V <- Tests].
  1754. horse_parse_if_match() ->
  1755. horse:repeat(200000,
  1756. parse_if_match(<<"\"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\"">>)
  1757. ).
  1758. -endif.
  1759. %% @doc Parse the If-Modified-Since header.
  1760. -spec parse_if_modified_since(binary()) -> calendar:datetime().
  1761. parse_if_modified_since(IfModifiedSince) ->
  1762. cow_date:parse_date(IfModifiedSince).
  1763. -ifdef(TEST).
  1764. parse_if_modified_since_test_() ->
  1765. Tests = [
  1766. {<<"Sat, 29 Oct 1994 19:43:31 GMT">>, {{1994, 10, 29}, {19, 43, 31}}}
  1767. ],
  1768. [{V, fun() -> R = parse_if_modified_since(V) end} || {V, R} <- Tests].
  1769. -endif.
  1770. %% @doc Parse the If-None-Match header.
  1771. -spec parse_if_none_match(binary()) -> '*' | [etag()].
  1772. parse_if_none_match(<<"*">>) ->
  1773. '*';
  1774. parse_if_none_match(IfNoneMatch) ->
  1775. nonempty(etag_list(IfNoneMatch, [])).
  1776. -ifdef(TEST).
  1777. parse_if_none_match_test_() ->
  1778. Tests = [
  1779. {<<"\"xyzzy\"">>, [{strong, <<"xyzzy">>}]},
  1780. {<<"W/\"xyzzy\"">>, [{weak, <<"xyzzy">>}]},
  1781. {<<"\"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\"">>,
  1782. [{strong, <<"xyzzy">>}, {strong, <<"r2d2xxxx">>}, {strong, <<"c3piozzzz">>}]},
  1783. {<<"W/\"xyzzy\", W/\"r2d2xxxx\", W/\"c3piozzzz\"">>,
  1784. [{weak, <<"xyzzy">>}, {weak, <<"r2d2xxxx">>}, {weak, <<"c3piozzzz">>}]},
  1785. {<<"*">>, '*'}
  1786. ],
  1787. [{V, fun() -> R = parse_if_none_match(V) end} || {V, R} <- Tests].
  1788. parse_if_none_match_error_test_() ->
  1789. Tests = [
  1790. <<>>
  1791. ],
  1792. [{V, fun() -> {'EXIT', _} = (catch parse_if_none_match(V)) end} || V <- Tests].
  1793. horse_parse_if_none_match() ->
  1794. horse:repeat(200000,
  1795. parse_if_none_match(<<"W/\"xyzzy\", W/\"r2d2xxxx\", W/\"c3piozzzz\"">>)
  1796. ).
  1797. -endif.
  1798. %% @doc Parse the If-Range header.
  1799. -spec parse_if_range(binary()) -> etag() | calendar:datetime().
  1800. parse_if_range(<< $W, $/, $", R/bits >>) ->
  1801. etag(R, weak, <<>>);
  1802. parse_if_range(<< $", R/bits >>) ->
  1803. etag(R, strong, <<>>);
  1804. parse_if_range(IfRange) ->
  1805. cow_date:parse_date(IfRange).
  1806. -ifdef(TEST).
  1807. parse_if_range_test_() ->
  1808. Tests = [
  1809. {<<"W/\"xyzzy\"">>, {weak, <<"xyzzy">>}},
  1810. {<<"\"xyzzy\"">>, {strong, <<"xyzzy">>}},
  1811. {<<"Sat, 29 Oct 1994 19:43:31 GMT">>, {{1994, 10, 29}, {19, 43, 31}}}
  1812. ],
  1813. [{V, fun() -> R = parse_if_range(V) end} || {V, R} <- Tests].
  1814. parse_if_range_error_test_() ->
  1815. Tests = [
  1816. <<>>
  1817. ],
  1818. [{V, fun() -> {'EXIT', _} = (catch parse_if_range(V)) end} || V <- Tests].
  1819. horse_parse_if_range_etag() ->
  1820. horse:repeat(200000,
  1821. parse_if_range(<<"\"xyzzy\"">>)
  1822. ).
  1823. horse_parse_if_range_date() ->
  1824. horse:repeat(200000,
  1825. parse_if_range(<<"Sat, 29 Oct 1994 19:43:31 GMT">>)
  1826. ).
  1827. -endif.
  1828. %% @doc Parse the If-Unmodified-Since header.
  1829. -spec parse_if_unmodified_since(binary()) -> calendar:datetime().
  1830. parse_if_unmodified_since(IfModifiedSince) ->
  1831. cow_date:parse_date(IfModifiedSince).
  1832. -ifdef(TEST).
  1833. parse_if_unmodified_since_test_() ->
  1834. Tests = [
  1835. {<<"Sat, 29 Oct 1994 19:43:31 GMT">>, {{1994, 10, 29}, {19, 43, 31}}}
  1836. ],
  1837. [{V, fun() -> R = parse_if_unmodified_since(V) end} || {V, R} <- Tests].
  1838. -endif.
  1839. %% @doc Parse the Last-Modified header.
  1840. -spec parse_last_modified(binary()) -> calendar:datetime().
  1841. parse_last_modified(LastModified) ->
  1842. cow_date:parse_date(LastModified).
  1843. -ifdef(TEST).
  1844. parse_last_modified_test_() ->
  1845. Tests = [
  1846. {<<"Tue, 15 Nov 1994 12:45:26 GMT">>, {{1994, 11, 15}, {12, 45, 26}}}
  1847. ],
  1848. [{V, fun() -> R = parse_last_modified(V) end} || {V, R} <- Tests].
  1849. -endif.
  1850. %% @doc Parse the Max-Forwards header.
  1851. -spec parse_max_forwards(binary()) -> non_neg_integer().
  1852. parse_max_forwards(MaxForwards) ->
  1853. I = binary_to_integer(MaxForwards),
  1854. true = I >= 0,
  1855. I.
  1856. -ifdef(TEST).
  1857. prop_parse_max_forwards() ->
  1858. ?FORALL(
  1859. X,
  1860. non_neg_integer(),
  1861. X =:= parse_max_forwards(integer_to_binary(X))
  1862. ).
  1863. parse_max_forwards_test_() ->
  1864. Tests = [
  1865. {<<"0">>, 0},
  1866. {<<"42">>, 42},
  1867. {<<"69">>, 69},
  1868. {<<"1337">>, 1337},
  1869. {<<"1234567890">>, 1234567890}
  1870. ],
  1871. [{V, fun() -> R = parse_max_forwards(V) end} || {V, R} <- Tests].
  1872. parse_max_forwards_error_test_() ->
  1873. Tests = [
  1874. <<>>,
  1875. <<"123, 123">>,
  1876. <<"4.17">>
  1877. ],
  1878. [{V, fun() -> {'EXIT', _} = (catch parse_max_forwards(V)) end} || V <- Tests].
  1879. -endif.
  1880. %% @doc Parse the Origin header.
  1881. %% According to the RFC6454 we should generate
  1882. %% a fresh globally unique identifier and return that value if:
  1883. %% - URI does not use a hierarchical element as a naming authority
  1884. %% or the URI is not an absolute URI
  1885. %% - the implementation doesn't support the protocol given by uri-scheme
  1886. %% Thus, erlang reference represents a GUID here.
  1887. %%
  1888. %% We only seek to have legal characters and separate the
  1889. %% host and port values. The number of segments in the host
  1890. %% or the size of each segment is not checked.
  1891. %%
  1892. %% There is no way to distinguish IPv4 addresses from regular
  1893. %% names until the last segment is reached therefore we do not
  1894. %% differentiate them.
  1895. %%
  1896. %% @todo The following valid hosts are currently rejected: IPv6
  1897. %% addresses with a zone identifier; IPvFuture addresses;
  1898. %% and percent-encoded addresses.
  1899. -spec parse_origin(binary()) -> [{binary(), binary(), 0..65535} | reference()].
  1900. parse_origin(Origins) ->
  1901. nonempty(origin_scheme(Origins, [])).
  1902. origin_scheme(<<>>, Acc) -> Acc;
  1903. origin_scheme(<< "http://", R/bits >>, Acc) -> origin_host(R, Acc, <<"http">>);
  1904. origin_scheme(<< "https://", R/bits >>, Acc) -> origin_host(R, Acc, <<"https">>);
  1905. origin_scheme(<< C, R/bits >>, Acc) when ?IS_TOKEN(C) -> origin_scheme(next_origin(R), [make_ref()|Acc]).
  1906. origin_host(<< $[, R/bits >>, Acc, Scheme) -> origin_ipv6_address(R, Acc, Scheme, << $[ >>);
  1907. origin_host(Host, Acc, Scheme) -> origin_reg_name(Host, Acc, Scheme, <<>>).
  1908. origin_ipv6_address(<< $] >>, Acc, Scheme, IP) ->
  1909. lists:reverse([{Scheme, << IP/binary, $] >>, default_port(Scheme)}|Acc]);
  1910. origin_ipv6_address(<< $], $\s, R/bits >>, Acc, Scheme, IP) ->
  1911. origin_scheme(R, [{Scheme, << IP/binary, $] >>, default_port(Scheme)}|Acc]);
  1912. origin_ipv6_address(<< $], $:, Port/bits >>, Acc, Scheme, IP) ->
  1913. origin_port(Port, Acc, Scheme, << IP/binary, $] >>, <<>>);
  1914. origin_ipv6_address(<< C, R/bits >>, Acc, Scheme, IP) when ?IS_HEX(C) or (C =:= $:) or (C =:= $.) ->
  1915. ?LOWER(origin_ipv6_address, R, Acc, Scheme, IP).
  1916. origin_reg_name(<<>>, Acc, Scheme, Name) ->
  1917. lists:reverse([{Scheme, Name, default_port(Scheme)}|Acc]);
  1918. origin_reg_name(<< $\s, R/bits >>, Acc, Scheme, Name) ->
  1919. origin_scheme(R, [{Scheme, Name, default_port(Scheme)}|Acc]);
  1920. origin_reg_name(<< $:, Port/bits >>, Acc, Scheme, Name) ->
  1921. origin_port(Port, Acc, Scheme, Name, <<>>);
  1922. origin_reg_name(<< C, R/bits >>, Acc, Scheme, Name) when ?IS_URI_UNRESERVED(C) or ?IS_URI_SUB_DELIMS(C) ->
  1923. ?LOWER(origin_reg_name, R, Acc, Scheme, Name).
  1924. origin_port(<<>>, Acc, Scheme, Host, Port) ->
  1925. lists:reverse([{Scheme, Host, binary_to_integer(Port)}|Acc]);
  1926. origin_port(<< $\s, R/bits >>, Acc, Scheme, Host, Port) ->
  1927. origin_scheme(R, [{Scheme, Host, binary_to_integer(Port)}|Acc]);
  1928. origin_port(<< C, R/bits >>, Acc, Scheme, Host, Port) when ?IS_DIGIT(C) ->
  1929. origin_port(R, Acc, Scheme, Host, << Port/binary, C >>).
  1930. next_origin(<<>>) -> <<>>;
  1931. next_origin(<< $\s, C, R/bits >>) when ?IS_TOKEN(C) -> << C, R/bits >>;
  1932. next_origin(<< C, R/bits >>) when ?IS_TOKEN(C) or (C =:= $:) or (C =:= $/) -> next_origin(R).
  1933. default_port(<< "http" >>) -> 80;
  1934. default_port(<< "https" >>) -> 443.
  1935. -ifdef(TEST).
  1936. scheme() -> oneof([<<"http">>, <<"https">>]).
  1937. scheme_host_port() ->
  1938. ?LET({Scheme, Host, Port},
  1939. {scheme(), host(), int(1, 65535)},
  1940. begin
  1941. HostBin = list_to_binary(Host),
  1942. {[{Scheme, ?LOWER(HostBin), Port}],
  1943. case default_port(Scheme) of
  1944. Port -> << Scheme/binary, "://", HostBin/binary>>;
  1945. _ -> << Scheme/binary, "://", HostBin/binary, $:, (integer_to_binary(Port))/binary >>
  1946. end}
  1947. end).
  1948. prop_parse_origin() ->
  1949. ?FORALL({Res, Origin}, scheme_host_port(), Res =:= parse_origin(Origin)).
  1950. parse_origin_test_() ->
  1951. Tests = [
  1952. {<<"http://www.example.org:8080">>, [{<<"http">>, <<"www.example.org">>, 8080}]},
  1953. {<<"http://www.example.org">>, [{<<"http">>, <<"www.example.org">>, 80}]},
  1954. {<<"http://192.0.2.1:8080">>, [{<<"http">>, <<"192.0.2.1">>, 8080}]},
  1955. {<<"http://192.0.2.1">>, [{<<"http">>, <<"192.0.2.1">>, 80}]},
  1956. {<<"http://[2001:db8::1]:8080">>, [{<<"http">>, <<"[2001:db8::1]">>, 8080}]},
  1957. {<<"http://[2001:db8::1]">>, [{<<"http">>, <<"[2001:db8::1]">>, 80}]},
  1958. {<<"http://[::ffff:192.0.2.1]:8080">>, [{<<"http">>, <<"[::ffff:192.0.2.1]">>, 8080}]},
  1959. {<<"http://[::ffff:192.0.2.1]">>, [{<<"http">>, <<"[::ffff:192.0.2.1]">>, 80}]},
  1960. {<<"http://example.org https://blue.example.com:8080">>,
  1961. [{<<"http">>, <<"example.org">>, 80},
  1962. {<<"https">>, <<"blue.example.com">>, 8080}]}
  1963. ],
  1964. [{V, fun() -> R = parse_origin(V) end} || {V, R} <- Tests].
  1965. parse_origin_reference_test_() ->
  1966. Tests = [
  1967. <<"null">>,
  1968. <<"httpx://example.org:80">>,
  1969. <<"httpx://example.org:80 null">>,
  1970. <<"null null">>
  1971. ],
  1972. [{V, fun() -> [true = is_reference(Ref) || Ref <- parse_origin(V)] end} || V <- Tests].
  1973. parse_origin_error_test_() ->
  1974. Tests = [
  1975. <<>>,
  1976. <<"null", $\t, "null">>,
  1977. <<"null", $\s, $\s, "null">>
  1978. ],
  1979. [{V, fun() -> {'EXIT', _} = (catch parse_origin(V)) end} || V <- Tests].
  1980. horse_parse_origin_blue_example_org() ->
  1981. horse:repeat(200000,
  1982. parse_origin(<<"http://blue.example.org:8080">>)
  1983. ).
  1984. horse_parse_origin_ipv4() ->
  1985. horse:repeat(200000,
  1986. parse_origin(<<"http://192.0.2.1:8080">>)
  1987. ).
  1988. horse_parse_origin_ipv6() ->
  1989. horse:repeat(200000,
  1990. parse_origin(<<"http://[2001:db8::1]:8080">>)
  1991. ).
  1992. horse_parse_origin_ipv6_v4() ->
  1993. horse:repeat(200000,
  1994. parse_origin(<<"http://[::ffff:192.0.2.1]:8080">>)
  1995. ).
  1996. horse_parse_origin_null() ->
  1997. horse:repeat(200000,
  1998. parse_origin(<<"null">>)
  1999. ).
  2000. -endif.
  2001. %% @doc Parse the Pragma header.
  2002. %%
  2003. %% Legacy header kept for backward compatibility with HTTP/1.0 caches.
  2004. %% Only the "no-cache" directive was ever specified, and only for
  2005. %% request messages.
  2006. %%
  2007. %% We take a large shortcut in the parsing of this header, expecting
  2008. %% an exact match of "no-cache".
  2009. -spec parse_pragma(binary()) -> cache | no_cache.
  2010. parse_pragma(<<"no-cache">>) -> no_cache;
  2011. parse_pragma(_) -> cache.
  2012. %% @doc Parse the Proxy-Authenticate header.
  2013. %%
  2014. %% Alias of parse_www_authenticate/1 due to identical syntax.
  2015. -spec parse_proxy_authenticate(binary()) -> [{basic, binary()}
  2016. | {bearer | digest | binary(), [{binary(), binary()}]}].
  2017. parse_proxy_authenticate(ProxyAuthenticate) ->
  2018. parse_www_authenticate(ProxyAuthenticate).
  2019. %% @doc Parse the Proxy-Authorization header.
  2020. %%
  2021. %% Alias of parse_authorization/1 due to identical syntax.
  2022. -spec parse_proxy_authorization(binary())
  2023. -> {basic, binary(), binary()}
  2024. | {bearer, binary()}
  2025. | {digest, [{binary(), binary()}]}.
  2026. parse_proxy_authorization(ProxyAuthorization) ->
  2027. parse_authorization(ProxyAuthorization).
  2028. %% @doc Parse the Range header.
  2029. -spec parse_range(binary())
  2030. -> {bytes, [{non_neg_integer(), non_neg_integer() | infinity} | neg_integer()]}
  2031. | {binary(), binary()}.
  2032. parse_range(<<"bytes=", R/bits >>) ->
  2033. bytes_range_set(R, []);
  2034. parse_range(<< C, R/bits >>) when ?IS_TOKEN(C) ->
  2035. ?LOWER(other_range_unit, R, <<>>).
  2036. bytes_range_set(<<>>, Acc) -> {bytes, lists:reverse(Acc)};
  2037. bytes_range_set(<< C, R/bits >>, Acc) when ?IS_WS_COMMA(C) -> bytes_range_set(R, Acc);
  2038. bytes_range_set(<< $-, C, R/bits >>, Acc) when ?IS_DIGIT(C) -> bytes_range_suffix_spec(R, Acc, C - $0);
  2039. bytes_range_set(<< C, R/bits >>, Acc) when ?IS_DIGIT(C) -> bytes_range_spec(R, Acc, C - $0).
  2040. bytes_range_spec(<< $-, C, R/bits >>, Acc, First) when ?IS_DIGIT(C) -> bytes_range_spec_last(R, Acc, First, C - $0);
  2041. bytes_range_spec(<< $-, R/bits >>, Acc, First) -> bytes_range_set_sep(R, [{First, infinity}|Acc]);
  2042. bytes_range_spec(<< C, R/bits >>, Acc, First) when ?IS_DIGIT(C) -> bytes_range_spec(R, Acc, First * 10 + C - $0).
  2043. bytes_range_spec_last(<< C, R/bits >>, Acc, First, Last) when ?IS_DIGIT(C) -> bytes_range_spec_last(R, Acc, First, Last * 10 + C - $0);
  2044. bytes_range_spec_last(R, Acc, First, Last) -> bytes_range_set_sep(R, [{First, Last}|Acc]).
  2045. bytes_range_suffix_spec(<< C, R/bits >>, Acc, Suffix) when ?IS_DIGIT(C) -> bytes_range_suffix_spec(R, Acc, Suffix * 10 + C - $0);
  2046. bytes_range_suffix_spec(R, Acc, Suffix) -> bytes_range_set_sep(R, [-Suffix|Acc]).
  2047. bytes_range_set_sep(<<>>, Acc) -> {bytes, lists:reverse(Acc)};
  2048. bytes_range_set_sep(<< C, R/bits >>, Acc) when ?IS_WS(C) -> bytes_range_set_sep(R, Acc);
  2049. bytes_range_set_sep(<< $,, R/bits >>, Acc) -> bytes_range_set(R, Acc).
  2050. other_range_unit(<< $=, C, R/bits >>, U) when ?IS_VCHAR(C) ->
  2051. other_range_set(R, U, << C >>);
  2052. other_range_unit(<< C, R/bits >>, U) when ?IS_TOKEN(C) ->
  2053. ?LOWER(other_range_unit, R, U).
  2054. other_range_set(<<>>, U, S) ->
  2055. {U, S};
  2056. other_range_set(<< C, R/bits >>, U, S) when ?IS_VCHAR(C) ->
  2057. other_range_set(R, U, << S/binary, C >>).
  2058. -ifdef(TEST).
  2059. bytes_range() ->
  2060. ?LET(BytesSet,
  2061. non_empty(list(oneof([
  2062. ?SUCHTHAT({First, Last}, {pos_integer(), pos_integer()}, First =< Last),
  2063. {pos_integer(), infinity},
  2064. ?LET(I, pos_integer(), -I)
  2065. ]))),
  2066. {{bytes, BytesSet}, begin
  2067. << _, Set/bits >> = iolist_to_binary([
  2068. case Spec of
  2069. {First, infinity} -> [$,, integer_to_binary(First), $-];
  2070. {First, Last} -> [$,, integer_to_binary(First), $-, integer_to_binary(Last)];
  2071. Suffix -> [$,, integer_to_binary(Suffix)]
  2072. end || Spec <- BytesSet]),
  2073. <<"bytes=", Set/binary >>
  2074. end}).
  2075. other_range() ->
  2076. ?LET(Range = {Unit, Set},
  2077. {token(), ?LET(L, non_empty(list(vchar())), list_to_binary(L))},
  2078. {Range, << Unit/binary, $=, Set/binary >>}).
  2079. range() ->
  2080. oneof([
  2081. bytes_range(),
  2082. other_range()
  2083. ]).
  2084. prop_parse_range() ->
  2085. ?FORALL({Range, RangeBin},
  2086. range(),
  2087. begin
  2088. Range2 = case Range of
  2089. {bytes, _} -> Range;
  2090. {Unit, Set} -> {?LOWER(Unit), Set}
  2091. end,
  2092. Range2 =:= parse_range(RangeBin)
  2093. end).
  2094. parse_range_test_() ->
  2095. Tests = [
  2096. {<<"bytes=0-499">>, {bytes, [{0, 499}]}},
  2097. {<<"bytes=500-999">>, {bytes, [{500, 999}]}},
  2098. {<<"bytes=-500">>, {bytes, [-500]}},
  2099. {<<"bytes=9500-">>, {bytes, [{9500, infinity}]}},
  2100. {<<"bytes=0-0,-1">>, {bytes, [{0, 0}, -1]}},
  2101. {<<"bytes=500-600,601-999">>, {bytes, [{500, 600}, {601, 999}]}},
  2102. {<<"bytes=500-700,601-999">>, {bytes, [{500, 700}, {601, 999}]}},
  2103. {<<"books=I-III,V-IX">>, {<<"books">>, <<"I-III,V-IX">>}}
  2104. ],
  2105. [{V, fun() -> R = parse_range(V) end} || {V, R} <- Tests].
  2106. parse_range_error_test_() ->
  2107. Tests = [
  2108. <<>>
  2109. ],
  2110. [{V, fun() -> {'EXIT', _} = (catch parse_range(V)) end} || V <- Tests].
  2111. horse_parse_range_first_last() ->
  2112. horse:repeat(200000,
  2113. parse_range(<<"bytes=500-999">>)
  2114. ).
  2115. horse_parse_range_infinity() ->
  2116. horse:repeat(200000,
  2117. parse_range(<<"bytes=9500-">>)
  2118. ).
  2119. horse_parse_range_suffix() ->
  2120. horse:repeat(200000,
  2121. parse_range(<<"bytes=-500">>)
  2122. ).
  2123. horse_parse_range_two() ->
  2124. horse:repeat(200000,
  2125. parse_range(<<"bytes=500-700,601-999">>)
  2126. ).
  2127. horse_parse_range_other() ->
  2128. horse:repeat(200000,
  2129. parse_range(<<"books=I-III,V-IX">>)
  2130. ).
  2131. -endif.
  2132. %% @doc Parse the Retry-After header.
  2133. -spec parse_retry_after(binary()) -> non_neg_integer() | calendar:datetime().
  2134. parse_retry_after(RetryAfter = << D, _/bits >>) when ?IS_DIGIT(D) ->
  2135. I = binary_to_integer(RetryAfter),
  2136. true = I >= 0,
  2137. I;
  2138. parse_retry_after(RetryAfter) ->
  2139. cow_date:parse_date(RetryAfter).
  2140. -ifdef(TEST).
  2141. parse_retry_after_test_() ->
  2142. Tests = [
  2143. {<<"Fri, 31 Dec 1999 23:59:59 GMT">>, {{1999, 12, 31}, {23, 59, 59}}},
  2144. {<<"120">>, 120}
  2145. ],
  2146. [{V, fun() -> R = parse_retry_after(V) end} || {V, R} <- Tests].
  2147. parse_retry_after_error_test_() ->
  2148. Tests = [
  2149. <<>>
  2150. ],
  2151. [{V, fun() -> {'EXIT', _} = (catch parse_retry_after(V)) end} || V <- Tests].
  2152. horse_parse_retry_after_date() ->
  2153. horse:repeat(200000,
  2154. parse_retry_after(<<"Fri, 31 Dec 1999 23:59:59 GMT">>)
  2155. ).
  2156. horse_parse_retry_after_delay_seconds() ->
  2157. horse:repeat(200000,
  2158. parse_retry_after(<<"120">>)
  2159. ).
  2160. -endif.
  2161. %% @doc Dummy parsing function for the Sec-WebSocket-Accept header.
  2162. %%
  2163. %% The argument is returned without any processing. This value is
  2164. %% expected to be matched directly by the client so no parsing is
  2165. %% needed.
  2166. -spec parse_sec_websocket_accept(binary()) -> binary().
  2167. parse_sec_websocket_accept(SecWebSocketAccept) ->
  2168. SecWebSocketAccept.
  2169. %% @doc Parse the Sec-WebSocket-Extensions request header.
  2170. -spec parse_sec_websocket_extensions(binary()) -> [{binary(), [binary() | {binary(), binary()}]}].
  2171. parse_sec_websocket_extensions(SecWebSocketExtensions) ->
  2172. nonempty(ws_extension_list(SecWebSocketExtensions, [])).
  2173. ws_extension_list(<<>>, Acc) -> lists:reverse(Acc);
  2174. ws_extension_list(<< C, R/bits >>, Acc) when ?IS_WS_COMMA(C) -> ws_extension_list(R, Acc);
  2175. ws_extension_list(<< C, R/bits >>, Acc) when ?IS_TOKEN(C) -> ws_extension(R, Acc, << C >>).
  2176. ws_extension(<< C, R/bits >>, Acc, E) when ?IS_TOKEN(C) -> ws_extension(R, Acc, << E/binary, C >>);
  2177. ws_extension(R, Acc, E) -> ws_extension_param_sep(R, Acc, E, []).
  2178. ws_extension_param_sep(<<>>, Acc, E, P) -> lists:reverse([{E, lists:reverse(P)}|Acc]);
  2179. ws_extension_param_sep(<< $,, R/bits >>, Acc, E, P) -> ws_extension_list(R, [{E, lists:reverse(P)}|Acc]);
  2180. ws_extension_param_sep(<< $;, R/bits >>, Acc, E, P) -> ws_extension_before_param(R, Acc, E, P);
  2181. ws_extension_param_sep(<< C, R/bits >>, Acc, E, P) when ?IS_WS(C) -> ws_extension_param_sep(R, Acc, E, P).
  2182. ws_extension_before_param(<< C, R/bits >>, Acc, E, P) when ?IS_WS(C) -> ws_extension_before_param(R, Acc, E, P);
  2183. ws_extension_before_param(<< C, R/bits >>, Acc, E, P) when ?IS_TOKEN(C) -> ws_extension_param(R, Acc, E, P, << C >>).
  2184. ws_extension_param(<< $=, $", R/bits >>, Acc, E, P, K) -> ws_extension_quoted(R, Acc, E, P, K, <<>>);
  2185. ws_extension_param(<< $=, C, R/bits >>, Acc, E, P, K) when ?IS_TOKEN(C) -> ws_extension_value(R, Acc, E, P, K, << C >>);
  2186. ws_extension_param(<< C, R/bits >>, Acc, E, P, K) when ?IS_TOKEN(C) -> ws_extension_param(R, Acc, E, P, << K/binary, C >>);
  2187. ws_extension_param(R, Acc, E, P, K) -> ws_extension_param_sep(R, Acc, E, [K|P]).
  2188. ws_extension_quoted(<< $", R/bits >>, Acc, E, P, K, V) -> ws_extension_param_sep(R, Acc, E, [{K, V}|P]);
  2189. ws_extension_quoted(<< $\\, C, R/bits >>, Acc, E, P, K, V) when ?IS_TOKEN(C) -> ws_extension_quoted(R, Acc, E, P, K, << V/binary, C >>);
  2190. ws_extension_quoted(<< C, R/bits >>, Acc, E, P, K, V) when ?IS_TOKEN(C) -> ws_extension_quoted(R, Acc, E, P, K, << V/binary, C >>).
  2191. ws_extension_value(<< C, R/bits >>, Acc, E, P, K, V) when ?IS_TOKEN(C) -> ws_extension_value(R, Acc, E, P, K, << V/binary, C >>);
  2192. ws_extension_value(R, Acc, E, P, K, V) -> ws_extension_param_sep(R, Acc, E, [{K, V}|P]).
  2193. -ifdef(TEST).
  2194. quoted_token() ->
  2195. ?LET(T,
  2196. non_empty(list(frequency([
  2197. {99, tchar()},
  2198. {1, [$\\, tchar()]}
  2199. ]))),
  2200. [$", T, $"]).
  2201. ws_extension() ->
  2202. ?LET({E, PL},
  2203. {token(), small_list({ows(), ows(), oneof([token(), {token(), oneof([token(), quoted_token()])}])})},
  2204. {E, PL, iolist_to_binary([E,
  2205. [case P of
  2206. {OWS1, OWS2, {K, V}} -> [OWS1, $;, OWS2, K, $=, V];
  2207. {OWS1, OWS2, K} -> [OWS1, $;, OWS2, K]
  2208. end || P <- PL]
  2209. ])}).
  2210. prop_parse_sec_websocket_extensions() ->
  2211. ?FORALL(L,
  2212. vector(1, 50, ws_extension()),
  2213. begin
  2214. << _, SecWebsocketExtensions/binary >> = iolist_to_binary([[$,, E] || {_, _, E} <- L]),
  2215. ResL = parse_sec_websocket_extensions(SecWebsocketExtensions),
  2216. CheckedL = [begin
  2217. ExpectedPL = [case P of
  2218. {_, _, {K, V}} -> {K, unquote(V)};
  2219. {_, _, K} -> K
  2220. end || P <- PL],
  2221. E =:= ResE andalso ExpectedPL =:= ResPL
  2222. end || {{E, PL, _}, {ResE, ResPL}} <- lists:zip(L, ResL)],
  2223. [true] =:= lists:usort(CheckedL)
  2224. end).
  2225. parse_sec_websocket_extensions_test_() ->
  2226. Tests = [
  2227. {<<"foo">>, [{<<"foo">>, []}]},
  2228. {<<"bar; baz=2">>, [{<<"bar">>, [{<<"baz">>, <<"2">>}]}]},
  2229. {<<"foo, bar; baz=2">>, [{<<"foo">>, []}, {<<"bar">>, [{<<"baz">>, <<"2">>}]}]},
  2230. {<<"deflate-stream">>, [{<<"deflate-stream">>, []}]},
  2231. {<<"mux; max-channels=4; flow-control, deflate-stream">>,
  2232. [{<<"mux">>, [{<<"max-channels">>, <<"4">>}, <<"flow-control">>]}, {<<"deflate-stream">>, []}]},
  2233. {<<"private-extension">>, [{<<"private-extension">>, []}]}
  2234. ],
  2235. [{V, fun() -> R = parse_sec_websocket_extensions(V) end} || {V, R} <- Tests].
  2236. parse_sec_websocket_extensions_error_test_() ->
  2237. Tests = [
  2238. <<>>
  2239. ],
  2240. [{V, fun() -> {'EXIT', _} = (catch parse_sec_websocket_extensions(V)) end}
  2241. || V <- Tests].
  2242. horse_parse_sec_websocket_extensions() ->
  2243. horse:repeat(200000,
  2244. parse_sec_websocket_extensions(<<"mux; max-channels=4; flow-control, deflate-stream">>)
  2245. ).
  2246. -endif.
  2247. %% @doc Dummy parsing function for the Sec-WebSocket-Key header.
  2248. %%
  2249. %% The argument is returned without any processing. This value is
  2250. %% expected to be prepended to a static value, the result of which
  2251. %% hashed to form a new base64 value returned in Sec-WebSocket-Accept,
  2252. %% therefore no parsing is needed.
  2253. -spec parse_sec_websocket_key(binary()) -> binary().
  2254. parse_sec_websocket_key(SecWebSocketKey) ->
  2255. SecWebSocketKey.
  2256. %% @doc Parse the Sec-WebSocket-Protocol request header.
  2257. -spec parse_sec_websocket_protocol_req(binary()) -> [binary()].
  2258. parse_sec_websocket_protocol_req(SecWebSocketProtocol) ->
  2259. nonempty(token_ci_list(SecWebSocketProtocol, [])).
  2260. -ifdef(TEST).
  2261. parse_sec_websocket_protocol_req_test_() ->
  2262. Tests = [
  2263. {<<"chat, superchat">>, [<<"chat">>, <<"superchat">>]}
  2264. ],
  2265. [{V, fun() -> R = parse_sec_websocket_protocol_req(V) end} || {V, R} <- Tests].
  2266. parse_sec_websocket_protocol_req_error_test_() ->
  2267. Tests = [
  2268. <<>>
  2269. ],
  2270. [{V, fun() -> {'EXIT', _} = (catch parse_sec_websocket_protocol_req(V)) end}
  2271. || V <- Tests].
  2272. horse_parse_sec_websocket_protocol_req() ->
  2273. horse:repeat(200000,
  2274. parse_sec_websocket_protocol_req(<<"chat, superchat">>)
  2275. ).
  2276. -endif.
  2277. %% @doc Parse the Sec-Websocket-Protocol response header.
  2278. -spec parse_sec_websocket_protocol_resp(binary()) -> binary().
  2279. parse_sec_websocket_protocol_resp(<< C, R/bits >>) when ?IS_TOKEN(C) ->
  2280. ?LOWER(token_ci, R, <<>>).
  2281. token_ci(<<>>, T) -> T;
  2282. token_ci(<< C, R/bits >>, T) when ?IS_TOKEN(C) ->
  2283. ?LOWER(token_ci, R, T).
  2284. -ifdef(TEST).
  2285. prop_parse_sec_websocket_protocol_resp() ->
  2286. ?FORALL(T,
  2287. token(),
  2288. ?LOWER(T) =:= parse_sec_websocket_protocol_resp(T)).
  2289. parse_sec_websocket_protocol_resp_test_() ->
  2290. Tests = [
  2291. {<<"chat">>, <<"chat">>},
  2292. {<<"CHAT">>, <<"chat">>}
  2293. ],
  2294. [{V, fun() -> R = parse_sec_websocket_protocol_resp(V) end} || {V, R} <- Tests].
  2295. parse_sec_websocket_protocol_resp_error_test_() ->
  2296. Tests = [
  2297. <<>>
  2298. ],
  2299. [{V, fun() -> {'EXIT', _} = (catch parse_sec_websocket_protocol_resp(V)) end}
  2300. || V <- Tests].
  2301. horse_parse_sec_websocket_protocol_resp() ->
  2302. horse:repeat(200000,
  2303. parse_sec_websocket_protocol_resp(<<"chat">>)
  2304. ).
  2305. -endif.
  2306. %% @doc Parse the Sec-WebSocket-Version request header.
  2307. -spec parse_sec_websocket_version_req(binary()) -> websocket_version().
  2308. parse_sec_websocket_version_req(SecWebSocketVersion) when byte_size(SecWebSocketVersion) < 4 ->
  2309. Version = binary_to_integer(SecWebSocketVersion),
  2310. true = Version >= 0 andalso Version =< 255,
  2311. Version.
  2312. -ifdef(TEST).
  2313. prop_parse_sec_websocket_version_req() ->
  2314. ?FORALL(Version,
  2315. int(0, 255),
  2316. Version =:= parse_sec_websocket_version_req(integer_to_binary(Version))).
  2317. parse_sec_websocket_version_req_test_() ->
  2318. Tests = [
  2319. {<<"13">>, 13},
  2320. {<<"25">>, 25}
  2321. ],
  2322. [{V, fun() -> R = parse_sec_websocket_version_req(V) end} || {V, R} <- Tests].
  2323. parse_sec_websocket_version_req_error_test_() ->
  2324. Tests = [
  2325. <<>>,
  2326. <<" ">>,
  2327. <<"7, 8, 13">>,
  2328. <<"invalid">>
  2329. ],
  2330. [{V, fun() -> {'EXIT', _} = (catch parse_sec_websocket_version_req(V)) end}
  2331. || V <- Tests].
  2332. horse_parse_sec_websocket_version_req_13() ->
  2333. horse:repeat(200000,
  2334. parse_sec_websocket_version_req(<<"13">>)
  2335. ).
  2336. horse_parse_sec_websocket_version_req_255() ->
  2337. horse:repeat(200000,
  2338. parse_sec_websocket_version_req(<<"255">>)
  2339. ).
  2340. -endif.
  2341. %% @doc Parse the Sec-WebSocket-Version response header.
  2342. -spec parse_sec_websocket_version_resp(binary()) -> [websocket_version()].
  2343. parse_sec_websocket_version_resp(SecWebSocketVersion) ->
  2344. nonempty(ws_version_list(SecWebSocketVersion, [])).
  2345. ws_version_list(<<>>, Acc) -> lists:reverse(Acc);
  2346. ws_version_list(<< C, R/bits >>, Acc) when ?IS_WS_COMMA(C) -> ws_version_list(R, Acc);
  2347. ws_version_list(<< C, R/bits >>, Acc) when ?IS_DIGIT(C) -> ws_version(R, Acc, C - $0).
  2348. ws_version(<< C, R/bits >>, Acc, V) when ?IS_DIGIT(C) -> ws_version(R, Acc, V * 10 + C - $0);
  2349. ws_version(R, Acc, V) -> ws_version_list_sep(R, [V|Acc]).
  2350. ws_version_list_sep(<<>>, Acc) -> lists:reverse(Acc);
  2351. ws_version_list_sep(<< C, R/bits >>, Acc) when ?IS_WS(C) -> ws_version_list_sep(R, Acc);
  2352. ws_version_list_sep(<< $,, R/bits >>, Acc) -> ws_version_list(R, Acc).
  2353. -ifdef(TEST).
  2354. sec_websocket_version_resp() ->
  2355. ?LET(L,
  2356. non_empty(list({ows(), ows(), int(0, 255)})),
  2357. begin
  2358. << _, SecWebSocketVersion/binary >> = iolist_to_binary(
  2359. [[OWS1, $,, OWS2, integer_to_binary(V)] || {OWS1, OWS2, V} <- L]),
  2360. {[V || {_, _, V} <- L], SecWebSocketVersion}
  2361. end).
  2362. prop_parse_sec_websocket_version_resp() ->
  2363. ?FORALL({L, SecWebSocketVersion},
  2364. sec_websocket_version_resp(),
  2365. L =:= parse_sec_websocket_version_resp(SecWebSocketVersion)).
  2366. parse_sec_websocket_version_resp_test_() ->
  2367. Tests = [
  2368. {<<"13, 8, 7">>, [13, 8, 7]}
  2369. ],
  2370. [{V, fun() -> R = parse_sec_websocket_version_resp(V) end} || {V, R} <- Tests].
  2371. parse_sec_websocket_version_resp_error_test_() ->
  2372. Tests = [
  2373. <<>>
  2374. ],
  2375. [{V, fun() -> {'EXIT', _} = (catch parse_sec_websocket_version_resp(V)) end}
  2376. || V <- Tests].
  2377. horse_parse_sec_websocket_version_resp() ->
  2378. horse:repeat(200000,
  2379. parse_sec_websocket_version_resp(<<"13, 8, 7">>)
  2380. ).
  2381. -endif.
  2382. %% @doc Parse the TE header.
  2383. %%
  2384. %% This function does not support parsing of transfer-parameter.
  2385. -spec parse_te(binary()) -> {trailers | no_trailers, [{binary(), qvalue()}]}.
  2386. parse_te(TE) ->
  2387. te_list(TE, no_trailers, []).
  2388. te_list(<<>>, Trail, Acc) -> {Trail, lists:reverse(Acc)};
  2389. te_list(<< C, R/bits >>, Trail, Acc) when ?IS_WS_COMMA(C) -> te_list(R, Trail, Acc);
  2390. te_list(<< "trailers", R/bits >>, Trail, Acc) -> te(R, Trail, Acc, <<"trailers">>);
  2391. te_list(<< "compress", R/bits >>, Trail, Acc) -> te(R, Trail, Acc, <<"compress">>);
  2392. te_list(<< "deflate", R/bits >>, Trail, Acc) -> te(R, Trail, Acc, <<"deflate">>);
  2393. te_list(<< "gzip", R/bits >>, Trail, Acc) -> te(R, Trail, Acc, <<"gzip">>);
  2394. te_list(<< C, R/bits >>, Trail, Acc) when ?IS_TOKEN(C) ->
  2395. ?LOWER(te, R, Trail, Acc, <<>>).
  2396. te(<<>>, _, Acc, <<"trailers">>) -> {trailers, lists:reverse(Acc)};
  2397. te(<< $,, R/bits >>, _, Acc, <<"trailers">>) -> te_list(R, trailers, Acc);
  2398. te(<< $;, R/bits >>, Trail, Acc, T) when T =/= <<"trailers">> -> te_before_weight(R, Trail, Acc, T);
  2399. te(<< C, R/bits >>, _, Acc, <<"trailers">>) when ?IS_WS(C) -> te_list_sep(R, trailers, Acc);
  2400. te(<< C, R/bits >>, Trail, Acc, T) when ?IS_TOKEN(C) ->
  2401. ?LOWER(te, R, Trail, Acc, T);
  2402. te(R, Trail, Acc, T) -> te_param_sep(R, Trail, Acc, T).
  2403. te_param_sep(<<>>, Trail, Acc, T) -> {Trail, lists:reverse([{T, 1000}|Acc])};
  2404. te_param_sep(<< $,, R/bits >>, Trail, Acc, T) -> te_list(R, Trail, [{T, 1000}|Acc]);
  2405. te_param_sep(<< C, R/bits >>, Trail, Acc, T) when ?IS_WS(C) -> te_param_sep(R, Trail, Acc, T).
  2406. te_before_weight(<< C, R/bits >>, Trail, Acc, T) when ?IS_WS(C) -> te_before_weight(R, Trail, Acc, T);
  2407. te_before_weight(<< $q, $=, R/bits >>, Trail, Acc, T) -> te_weight(R, Trail, Acc, T).
  2408. te_weight(<< "1.000", R/bits >>, Trail, Acc, T) -> te_list_sep(R, Trail, [{T, 1000}|Acc]);
  2409. te_weight(<< "1.00", R/bits >>, Trail, Acc, T) -> te_list_sep(R, Trail, [{T, 1000}|Acc]);
  2410. te_weight(<< "1.0", R/bits >>, Trail, Acc, T) -> te_list_sep(R, Trail, [{T, 1000}|Acc]);
  2411. te_weight(<< "1.", R/bits >>, Trail, Acc, T) -> te_list_sep(R, Trail, [{T, 1000}|Acc]);
  2412. te_weight(<< "1", R/bits >>, Trail, Acc, T) -> te_list_sep(R, Trail, [{T, 1000}|Acc]);
  2413. te_weight(<< "0.", A, B, C, R/bits >>, Trail, Acc, T) when ?IS_DIGIT(A), ?IS_DIGIT(B), ?IS_DIGIT(C) ->
  2414. te_list_sep(R, Trail, [{T, (A - $0) * 100 + (B - $0) * 10 + (C - $0)}|Acc]);
  2415. te_weight(<< "0.", A, B, R/bits >>, Trail, Acc, T) when ?IS_DIGIT(A), ?IS_DIGIT(B) ->
  2416. te_list_sep(R, Trail, [{T, (A - $0) * 100 + (B - $0) * 10}|Acc]);
  2417. te_weight(<< "0.", A, R/bits >>, Trail, Acc, T) when ?IS_DIGIT(A) ->
  2418. te_list_sep(R, Trail, [{T, (A - $0) * 100}|Acc]);
  2419. te_weight(<< "0.", R/bits >>, Trail, Acc, T) -> te_list_sep(R, Trail, [{T, 0}|Acc]);
  2420. te_weight(<< "0", R/bits >>, Trail, Acc, T) -> te_list_sep(R, Trail, [{T, 0}|Acc]).
  2421. te_list_sep(<<>>, Trail, Acc) -> {Trail, lists:reverse(Acc)};
  2422. te_list_sep(<< C, R/bits >>, Trail, Acc) when ?IS_WS(C) -> te_list_sep(R, Trail, Acc);
  2423. te_list_sep(<< $,, R/bits >>, Trail, Acc) -> te_list(R, Trail, Acc).
  2424. -ifdef(TEST).
  2425. te() ->
  2426. ?LET({Trail, L},
  2427. {elements([trailers, no_trailers]),
  2428. small_non_empty_list({?SUCHTHAT(T, token(), T =/= <<"trailers">>), weight()})},
  2429. {Trail, L, begin
  2430. L2 = case Trail of
  2431. no_trailers -> L;
  2432. trailers ->
  2433. Rand = random:uniform(length(L) + 1) - 1,
  2434. {Before, After} = lists:split(Rand, L),
  2435. Before ++ [{<<"trailers">>, undefined}|After]
  2436. end,
  2437. << _, TE/binary >> = iolist_to_binary([case W of
  2438. undefined -> [$,, T];
  2439. _ -> [$,, T, <<";q=">>, qvalue_to_iodata(W)]
  2440. end || {T, W} <- L2]),
  2441. TE
  2442. end}
  2443. ).
  2444. prop_parse_te() ->
  2445. random:seed(os:timestamp()),
  2446. ?FORALL({Trail, L, TE},
  2447. te(),
  2448. begin
  2449. {ResTrail, ResL} = parse_te(TE),
  2450. CheckedL = [begin
  2451. ResT =:= ?LOWER(T)
  2452. andalso (ResW =:= W orelse (W =:= undefined andalso ResW =:= 1000))
  2453. end || {{T, W}, {ResT, ResW}} <- lists:zip(L, ResL)],
  2454. ResTrail =:= Trail andalso [true] =:= lists:usort(CheckedL)
  2455. end).
  2456. parse_te_test_() ->
  2457. Tests = [
  2458. {<<"deflate">>, {no_trailers, [{<<"deflate">>, 1000}]}},
  2459. {<<>>, {no_trailers, []}},
  2460. {<<"trailers, deflate;q=0.5">>, {trailers, [{<<"deflate">>, 500}]}}
  2461. ],
  2462. [{V, fun() -> R = parse_te(V) end} || {V, R} <- Tests].
  2463. horse_parse_te() ->
  2464. horse:repeat(200000,
  2465. parse_te(<<"trailers, deflate;q=0.5">>)
  2466. ).
  2467. -endif.
  2468. %% @doc Parse the Trailer header.
  2469. -spec parse_trailer(binary()) -> [binary()].
  2470. parse_trailer(Trailer) ->
  2471. nonempty(token_ci_list(Trailer, [])).
  2472. -ifdef(TEST).
  2473. parse_trailer_test_() ->
  2474. Tests = [
  2475. {<<"Date, Content-MD5">>, [<<"date">>, <<"content-md5">>]}
  2476. ],
  2477. [{V, fun() -> R = parse_trailer(V) end} || {V, R} <- Tests].
  2478. parse_trailer_error_test_() ->
  2479. Tests = [
  2480. <<>>
  2481. ],
  2482. [{V, fun() -> {'EXIT', _} = (catch parse_trailer(V)) end} || V <- Tests].
  2483. horse_parse_trailer() ->
  2484. horse:repeat(200000,
  2485. parse_trailer(<<"Date, Content-MD5">>)
  2486. ).
  2487. -endif.
  2488. %% @doc Parse the Transfer-Encoding header.
  2489. %%
  2490. %% This function does not support parsing of transfer-parameter.
  2491. -spec parse_transfer_encoding(binary()) -> [binary()].
  2492. parse_transfer_encoding(<<"chunked">>) ->
  2493. [<<"chunked">>];
  2494. parse_transfer_encoding(TransferEncoding) ->
  2495. nonempty(token_ci_list(TransferEncoding, [])).
  2496. -ifdef(TEST).
  2497. prop_parse_transfer_encoding() ->
  2498. ?FORALL(L,
  2499. non_empty(list(token())),
  2500. begin
  2501. << _, TransferEncoding/binary >> = iolist_to_binary([[$,, C] || C <- L]),
  2502. ResL = parse_transfer_encoding(TransferEncoding),
  2503. CheckedL = [?LOWER(Co) =:= ResC || {Co, ResC} <- lists:zip(L, ResL)],
  2504. [true] =:= lists:usort(CheckedL)
  2505. end).
  2506. parse_transfer_encoding_test_() ->
  2507. Tests = [
  2508. {<<"a , , , ">>, [<<"a">>]},
  2509. {<<" , , , a">>, [<<"a">>]},
  2510. {<<"a , , b">>, [<<"a">>, <<"b">>]},
  2511. {<<"chunked">>, [<<"chunked">>]},
  2512. {<<"chunked, something">>, [<<"chunked">>, <<"something">>]},
  2513. {<<"gzip, chunked">>, [<<"gzip">>, <<"chunked">>]}
  2514. ],
  2515. [{V, fun() -> R = parse_transfer_encoding(V) end} || {V, R} <- Tests].
  2516. parse_transfer_encoding_error_test_() ->
  2517. Tests = [
  2518. <<>>,
  2519. <<" ">>,
  2520. <<" , ">>,
  2521. <<",,,">>,
  2522. <<"a b">>
  2523. ],
  2524. [{V, fun() -> {'EXIT', _} = (catch parse_transfer_encoding(V)) end}
  2525. || V <- Tests].
  2526. horse_parse_transfer_encoding_chunked() ->
  2527. horse:repeat(200000,
  2528. parse_transfer_encoding(<<"chunked">>)
  2529. ).
  2530. horse_parse_transfer_encoding_custom() ->
  2531. horse:repeat(200000,
  2532. parse_transfer_encoding(<<"chunked, something">>)
  2533. ).
  2534. -endif.
  2535. %% @doc Parse the Upgrade header.
  2536. %%
  2537. %% It is unclear from the RFC whether the values here are
  2538. %% case sensitive.
  2539. %%
  2540. %% We handle them in a case insensitive manner because they
  2541. %% are described as case insensitive in the Websocket RFC.
  2542. -spec parse_upgrade(binary()) -> [binary()].
  2543. parse_upgrade(Upgrade) ->
  2544. nonempty(protocol_list(Upgrade, [])).
  2545. protocol_list(<<>>, Acc) -> lists:reverse(Acc);
  2546. protocol_list(<< C, R/bits >>, Acc) when ?IS_WS_COMMA(C) -> protocol_list(R, Acc);
  2547. protocol_list(<< C, R/bits >>, Acc) when ?IS_TOKEN(C) ->
  2548. ?LOWER(protocol_name, R, Acc, <<>>).
  2549. protocol_name(<< $/, C, R/bits >>, Acc, P) ->
  2550. ?LOWER(protocol_version, R, Acc, << P/binary, $/ >>);
  2551. protocol_name(<< C, R/bits >>, Acc, P) when ?IS_TOKEN(C) ->
  2552. ?LOWER(protocol_name, R, Acc, P);
  2553. protocol_name(R, Acc, P) -> protocol_list_sep(R, [P|Acc]).
  2554. protocol_version(<< C, R/bits >>, Acc, P) when ?IS_TOKEN(C) ->
  2555. ?LOWER(protocol_version, R, Acc, P);
  2556. protocol_version(R, Acc, P) -> protocol_list_sep(R, [P|Acc]).
  2557. protocol_list_sep(<<>>, Acc) -> lists:reverse(Acc);
  2558. protocol_list_sep(<< C, R/bits >>, Acc) when ?IS_WS(C) -> protocol_list_sep(R, Acc);
  2559. protocol_list_sep(<< $,, R/bits >>, Acc) -> protocol_list(R, Acc).
  2560. -ifdef(TEST).
  2561. protocols() ->
  2562. ?LET(P,
  2563. oneof([token(), [token(), $/, token()]]),
  2564. iolist_to_binary(P)).
  2565. prop_parse_upgrade() ->
  2566. ?FORALL(L,
  2567. non_empty(list(protocols())),
  2568. begin
  2569. << _, Upgrade/binary >> = iolist_to_binary([[$,, P] || P <- L]),
  2570. ResL = parse_upgrade(Upgrade),
  2571. CheckedL = [?LOWER(P) =:= ResP || {P, ResP} <- lists:zip(L, ResL)],
  2572. [true] =:= lists:usort(CheckedL)
  2573. end).
  2574. parse_upgrade_test_() ->
  2575. Tests = [
  2576. {<<"HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11">>,
  2577. [<<"http/2.0">>, <<"shttp/1.3">>, <<"irc/6.9">>, <<"rta/x11">>]},
  2578. {<<"HTTP/2.0">>, [<<"http/2.0">>]}
  2579. ],
  2580. [{V, fun() -> R = parse_upgrade(V) end} || {V, R} <- Tests].
  2581. parse_upgrade_error_test_() ->
  2582. Tests = [
  2583. <<>>
  2584. ],
  2585. [{V, fun() -> {'EXIT', _} = (catch parse_upgrade(V)) end}
  2586. || V <- Tests].
  2587. -endif.
  2588. %% @doc Parse the Vary header.
  2589. -spec parse_vary(binary()) -> '*' | [binary()].
  2590. parse_vary(<<"*">>) ->
  2591. '*';
  2592. parse_vary(Vary) ->
  2593. nonempty(token_ci_list(Vary, [])).
  2594. -ifdef(TEST).
  2595. parse_vary_test_() ->
  2596. Tests = [
  2597. {<<"*">>, '*'},
  2598. {<<"Accept-Encoding">>, [<<"accept-encoding">>]},
  2599. {<<"accept-encoding, accept-language">>, [<<"accept-encoding">>, <<"accept-language">>]}
  2600. ],
  2601. [{V, fun() -> R = parse_vary(V) end} || {V, R} <- Tests].
  2602. parse_vary_error_test_() ->
  2603. Tests = [
  2604. <<>>
  2605. ],
  2606. [{V, fun() -> {'EXIT', _} = (catch parse_vary(V)) end} || V <- Tests].
  2607. -endif.
  2608. %% @doc Parse the WWW-Authenticate header.
  2609. %%
  2610. %% Unknown schemes are represented as the lowercase binary
  2611. %% instead of an atom. Unlike with parse_authorization/1,
  2612. %% we do not crash on unknown schemes.
  2613. %%
  2614. %% When parsing auth-params, we do not accept BWS characters around the "=".
  2615. -spec parse_www_authenticate(binary()) -> [{basic, binary()}
  2616. | {bearer | digest | binary(), [{binary(), binary()}]}].
  2617. parse_www_authenticate(Authenticate) ->
  2618. nonempty(www_auth_list(Authenticate, [])).
  2619. www_auth_list(<<>>, Acc) -> lists:reverse(Acc);
  2620. www_auth_list(<< C, R/bits >>, Acc) when ?IS_WS_COMMA(C) -> www_auth_list(R, Acc);
  2621. www_auth_list(<< C, R/bits >>, Acc) when ?IS_TOKEN(C) ->
  2622. ?LOWER(www_auth_scheme, R, Acc, <<>>).
  2623. www_auth_basic_before_realm(<< C, R/bits >>, Acc) when ?IS_WS(C) -> www_auth_basic_before_realm(R, Acc);
  2624. www_auth_basic_before_realm(<< "realm=\"", R/bits >>, Acc) -> www_auth_basic(R, Acc, <<>>).
  2625. www_auth_basic(<< $", R/bits >>, Acc, Realm) -> www_auth_list_sep(R, [{basic, Realm}|Acc]);
  2626. www_auth_basic(<< $\\, C, R/bits >>, Acc, Realm) when ?IS_VCHAR_OBS(C) -> www_auth_basic(R, Acc, << Realm/binary, C >>);
  2627. www_auth_basic(<< C, R/bits >>, Acc, Realm) when ?IS_VCHAR_OBS(C) -> www_auth_basic(R, Acc, << Realm/binary, C >>).
  2628. www_auth_scheme(<< C, R/bits >>, Acc, Scheme) when ?IS_WS(C) ->
  2629. case Scheme of
  2630. <<"basic">> -> www_auth_basic_before_realm(R, Acc);
  2631. <<"bearer">> -> www_auth_params_list(R, Acc, bearer, []);
  2632. <<"digest">> -> www_auth_params_list(R, Acc, digest, []);
  2633. _ -> www_auth_params_list(R, Acc, Scheme, [])
  2634. end;
  2635. www_auth_scheme(<< C, R/bits >>, Acc, Scheme) when ?IS_TOKEN(C) ->
  2636. ?LOWER(www_auth_scheme, R, Acc, Scheme).
  2637. www_auth_list_sep(<<>>, Acc) -> lists:reverse(Acc);
  2638. www_auth_list_sep(<< C, R/bits >>, Acc) when ?IS_WS(C) -> www_auth_list_sep(R, Acc);
  2639. www_auth_list_sep(<< $,, R/bits >>, Acc) -> www_auth_list(R, Acc).
  2640. www_auth_params_list(<<>>, Acc, Scheme, Params) ->
  2641. lists:reverse([{Scheme, lists:reverse(nonempty(Params))}|Acc]);
  2642. www_auth_params_list(<< C, R/bits >>, Acc, Scheme, Params) when ?IS_WS_COMMA(C) ->
  2643. www_auth_params_list(R, Acc, Scheme, Params);
  2644. www_auth_params_list(<< "algorithm=", C, R/bits >>, Acc, Scheme, Params) when ?IS_TOKEN(C) ->
  2645. www_auth_token(R, Acc, Scheme, Params, <<"algorithm">>, << C >>);
  2646. www_auth_params_list(<< "domain=\"", R/bits >>, Acc, Scheme, Params) ->
  2647. www_auth_quoted(R, Acc, Scheme, Params, <<"domain">>, <<>>);
  2648. www_auth_params_list(<< "error=\"", R/bits >>, Acc, Scheme, Params) ->
  2649. www_auth_quoted(R, Acc, Scheme, Params, <<"error">>, <<>>);
  2650. www_auth_params_list(<< "error_description=\"", R/bits >>, Acc, Scheme, Params) ->
  2651. www_auth_quoted(R, Acc, Scheme, Params, <<"error_description">>, <<>>);
  2652. www_auth_params_list(<< "error_uri=\"", R/bits >>, Acc, Scheme, Params) ->
  2653. www_auth_quoted(R, Acc, Scheme, Params, <<"error_uri">>, <<>>);
  2654. www_auth_params_list(<< "nonce=\"", R/bits >>, Acc, Scheme, Params) ->
  2655. www_auth_quoted(R, Acc, Scheme, Params, <<"nonce">>, <<>>);
  2656. www_auth_params_list(<< "opaque=\"", R/bits >>, Acc, Scheme, Params) ->
  2657. www_auth_quoted(R, Acc, Scheme, Params, <<"opaque">>, <<>>);
  2658. www_auth_params_list(<< "qop=\"", R/bits >>, Acc, Scheme, Params) ->
  2659. www_auth_quoted(R, Acc, Scheme, Params, <<"qop">>, <<>>);
  2660. www_auth_params_list(<< "realm=\"", R/bits >>, Acc, Scheme, Params) ->
  2661. www_auth_quoted(R, Acc, Scheme, Params, <<"realm">>, <<>>);
  2662. www_auth_params_list(<< "scope=\"", R/bits >>, Acc, Scheme, Params) ->
  2663. www_auth_quoted(R, Acc, Scheme, Params, <<"scope">>, <<>>);
  2664. www_auth_params_list(<< "stale=false", R/bits >>, Acc, Scheme, Params) ->
  2665. www_auth_params_list_sep(R, Acc, Scheme, [{<<"stale">>, <<"false">>}|Params]);
  2666. www_auth_params_list(<< "stale=true", R/bits >>, Acc, Scheme, Params) ->
  2667. www_auth_params_list_sep(R, Acc, Scheme, [{<<"stale">>, <<"true">>}|Params]);
  2668. www_auth_params_list(<< C, R/bits >>, Acc, Scheme, Params) when ?IS_TOKEN(C) ->
  2669. ?LOWER(www_auth_param, R, Acc, Scheme, Params, <<>>).
  2670. www_auth_param(<< $=, $", R/bits >>, Acc, Scheme, Params, K) ->
  2671. www_auth_quoted(R, Acc, Scheme, Params, K, <<>>);
  2672. www_auth_param(<< $=, C, R/bits >>, Acc, Scheme, Params, K) when ?IS_TOKEN(C) ->
  2673. www_auth_token(R, Acc, Scheme, Params, K, << C >>);
  2674. www_auth_param(<< C, R/bits >>, Acc, Scheme, Params, K) when ?IS_TOKEN(C) ->
  2675. ?LOWER(www_auth_param, R, Acc, Scheme, Params, K);
  2676. www_auth_param(R, Acc, Scheme, Params, NewScheme) ->
  2677. www_auth_scheme(R, [{Scheme, lists:reverse(Params)}|Acc], NewScheme).
  2678. www_auth_token(<< C, R/bits >>, Acc, Scheme, Params, K, V) when ?IS_TOKEN(C) ->
  2679. www_auth_token(R, Acc, Scheme, Params, K, << V/binary, C >>);
  2680. www_auth_token(R, Acc, Scheme, Params, K, V) ->
  2681. www_auth_params_list_sep(R, Acc, Scheme, [{K, V}|Params]).
  2682. www_auth_quoted(<< $", R/bits >>, Acc, Scheme, Params, K, V) ->
  2683. www_auth_params_list_sep(R, Acc, Scheme, [{K, V}|Params]);
  2684. www_auth_quoted(<< $\\, C, R/bits >>, Acc, Scheme, Params, K, V) when ?IS_VCHAR_OBS(C) ->
  2685. www_auth_quoted(R, Acc, Scheme, Params, K, << V/binary, C >>);
  2686. www_auth_quoted(<< C, R/bits >>, Acc, Scheme, Params, K, V) when ?IS_VCHAR_OBS(C) ->
  2687. www_auth_quoted(R, Acc, Scheme, Params, K, << V/binary, C >>).
  2688. www_auth_params_list_sep(<<>>, Acc, Scheme, Params) ->
  2689. lists:reverse([{Scheme, lists:reverse(Params)}|Acc]);
  2690. www_auth_params_list_sep(<< C, R/bits >>, Acc, Scheme, Params) when ?IS_WS(C) ->
  2691. www_auth_params_list_sep(R, Acc, Scheme, Params);
  2692. www_auth_params_list_sep(<< $,, R/bits >>, Acc, Scheme, Params) ->
  2693. www_auth_params_list_after_sep(R, Acc, Scheme, Params).
  2694. www_auth_params_list_after_sep(<<>>, Acc, Scheme, Params) ->
  2695. lists:reverse([{Scheme, lists:reverse(Params)}|Acc]);
  2696. www_auth_params_list_after_sep(<< C, R/bits >>, Acc, Scheme, Params) when ?IS_WS_COMMA(C) ->
  2697. www_auth_params_list_after_sep(R, Acc, Scheme, Params);
  2698. www_auth_params_list_after_sep(R, Acc, Scheme, Params) ->
  2699. www_auth_params_list(R, Acc, Scheme, Params).
  2700. -ifdef(TEST).
  2701. parse_www_authenticate_test_() ->
  2702. Tests = [
  2703. {<<"Newauth realm=\"apps\", type=1, title=\"Login to \\\"apps\\\"\", Basic realm=\"simple\"">>,
  2704. [{<<"newauth">>, [
  2705. {<<"realm">>, <<"apps">>},
  2706. {<<"type">>, <<"1">>},
  2707. {<<"title">>, <<"Login to \"apps\"">>}]},
  2708. {basic, <<"simple">>}]},
  2709. %% Same test, different order.
  2710. {<<"Basic realm=\"simple\", Newauth realm=\"apps\", type=1, title=\"Login to \\\"apps\\\"\"">>,
  2711. [{basic, <<"simple">>},
  2712. {<<"newauth">>, [
  2713. {<<"realm">>, <<"apps">>},
  2714. {<<"type">>, <<"1">>},
  2715. {<<"title">>, <<"Login to \"apps\"">>}]}]},
  2716. {<<"Bearer realm=\"example\"">>,
  2717. [{bearer, [{<<"realm">>, <<"example">>}]}]},
  2718. {<<"Bearer realm=\"example\", error=\"invalid_token\", error_description=\"The access token expired\"">>,
  2719. [{bearer, [
  2720. {<<"realm">>, <<"example">>},
  2721. {<<"error">>, <<"invalid_token">>},
  2722. {<<"error_description">>, <<"The access token expired">>}
  2723. ]}]},
  2724. {<<"Basic realm=\"WallyWorld\"">>,
  2725. [{basic, <<"WallyWorld">>}]},
  2726. {<<"Digest realm=\"testrealm@host.com\", qop=\"auth,auth-int\", "
  2727. "nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\", "
  2728. "opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"">>,
  2729. [{digest, [
  2730. {<<"realm">>, <<"testrealm@host.com">>},
  2731. {<<"qop">>, <<"auth,auth-int">>},
  2732. {<<"nonce">>, <<"dcd98b7102dd2f0e8b11d0f600bfb0c093">>},
  2733. {<<"opaque">>, <<"5ccc069c403ebaf9f0171e9517f40e41">>}
  2734. ]}]}
  2735. ],
  2736. [{V, fun() -> R = parse_www_authenticate(V) end} || {V, R} <- Tests].
  2737. parse_www_authenticate_error_test_() ->
  2738. Tests = [
  2739. <<>>
  2740. ],
  2741. [{V, fun() -> {'EXIT', _} = (catch parse_www_authenticate(V)) end} || V <- Tests].
  2742. horse_parse_www_authenticate() ->
  2743. horse:repeat(200000,
  2744. parse_www_authenticate(<<"Newauth realm=\"apps\", type=1, title=\"Login to \\\"apps\\\"\", Basic realm=\"simple\"">>)
  2745. ).
  2746. -endif.
  2747. %% @doc Parse the X-Forwarded-For header.
  2748. %%
  2749. %% This header has no specification but *looks like* it is
  2750. %% a list of tokens.
  2751. %%
  2752. %% This header is deprecated in favor of the Forwarded header.
  2753. -spec parse_x_forwarded_for(binary()) -> [binary()].
  2754. parse_x_forwarded_for(XForwardedFor) ->
  2755. nonempty(token_list(XForwardedFor, [])).
  2756. -ifdef(TEST).
  2757. parse_x_forwarded_for_test_() ->
  2758. Tests = [
  2759. {<<"client, proxy1, proxy2">>, [<<"client">>, <<"proxy1">>, <<"proxy2">>]},
  2760. {<<"128.138.243.150, unknown, 192.52.106.30">>, [<<"128.138.243.150">>, <<"unknown">>, <<"192.52.106.30">>]}
  2761. ],
  2762. [{V, fun() -> R = parse_x_forwarded_for(V) end} || {V, R} <- Tests].
  2763. parse_x_forwarded_for_error_test_() ->
  2764. Tests = [
  2765. <<>>
  2766. ],
  2767. [{V, fun() -> {'EXIT', _} = (catch parse_x_forwarded_for(V)) end} || V <- Tests].
  2768. -endif.
  2769. %% Building.
  2770. %% @doc Build the Access-Control-Allow-Credentials header.
  2771. -spec access_control_allow_credentials() -> iodata().
  2772. access_control_allow_credentials() -> <<"true">>.
  2773. %% @doc Build the Access-Control-Allow-Headers header.
  2774. -spec access_control_allow_headers([binary()]) -> iodata().
  2775. access_control_allow_headers(Headers) ->
  2776. join_token_list(nonempty(Headers)).
  2777. -ifdef(TEST).
  2778. access_control_allow_headers_test_() ->
  2779. Tests = [
  2780. {[<<"accept">>], <<"accept">>},
  2781. {[<<"accept">>, <<"authorization">>, <<"content-type">>], <<"accept, authorization, content-type">>}
  2782. ],
  2783. [{lists:flatten(io_lib:format("~p", [V])),
  2784. fun() -> R = iolist_to_binary(access_control_allow_headers(V)) end} || {V, R} <- Tests].
  2785. access_control_allow_headers_error_test_() ->
  2786. Tests = [
  2787. []
  2788. ],
  2789. [{lists:flatten(io_lib:format("~p", [V])),
  2790. fun() -> {'EXIT', _} = (catch access_control_allow_headers(V)) end} || V <- Tests].
  2791. horse_access_control_allow_headers() ->
  2792. horse:repeat(200000,
  2793. access_control_allow_headers([<<"accept">>, <<"authorization">>, <<"content-type">>])
  2794. ).
  2795. -endif.
  2796. %% @doc Build the Access-Control-Allow-Methods header.
  2797. -spec access_control_allow_methods([binary()]) -> iodata().
  2798. access_control_allow_methods(Methods) ->
  2799. join_token_list(nonempty(Methods)).
  2800. -ifdef(TEST).
  2801. access_control_allow_methods_test_() ->
  2802. Tests = [
  2803. {[<<"GET">>], <<"GET">>},
  2804. {[<<"GET">>, <<"POST">>, <<"DELETE">>], <<"GET, POST, DELETE">>}
  2805. ],
  2806. [{lists:flatten(io_lib:format("~p", [V])),
  2807. fun() -> R = iolist_to_binary(access_control_allow_methods(V)) end} || {V, R} <- Tests].
  2808. access_control_allow_methods_error_test_() ->
  2809. Tests = [
  2810. []
  2811. ],
  2812. [{lists:flatten(io_lib:format("~p", [V])),
  2813. fun() -> {'EXIT', _} = (catch access_control_allow_methods(V)) end} || V <- Tests].
  2814. horse_access_control_allow_methods() ->
  2815. horse:repeat(200000,
  2816. access_control_allow_methods([<<"GET">>, <<"POST">>, <<"DELETE">>])
  2817. ).
  2818. -endif.
  2819. %% @doc Build the Access-Control-Allow-Origin header.
  2820. -spec access_control_allow_origin({binary(), binary(), 0..65535} | reference() | '*') -> iodata().
  2821. access_control_allow_origin({Scheme, Host, Port}) ->
  2822. case default_port(Scheme) of
  2823. Port -> [Scheme, <<"://">>, Host];
  2824. _ -> [Scheme, <<"://">>, Host, <<":">>, integer_to_binary(Port)]
  2825. end;
  2826. access_control_allow_origin('*') -> <<$*>>;
  2827. access_control_allow_origin(Ref) when is_reference(Ref) -> <<"null">>.
  2828. -ifdef(TEST).
  2829. access_control_allow_origin_test_() ->
  2830. Tests = [
  2831. {{<<"http">>, <<"www.example.org">>, 8080}, <<"http://www.example.org:8080">>},
  2832. {{<<"http">>, <<"www.example.org">>, 80}, <<"http://www.example.org">>},
  2833. {{<<"http">>, <<"192.0.2.1">>, 8080}, <<"http://192.0.2.1:8080">>},
  2834. {{<<"http">>, <<"192.0.2.1">>, 80}, <<"http://192.0.2.1">>},
  2835. {{<<"http">>, <<"[2001:db8::1]">>, 8080}, <<"http://[2001:db8::1]:8080">>},
  2836. {{<<"http">>, <<"[2001:db8::1]">>, 80}, <<"http://[2001:db8::1]">>},
  2837. {{<<"http">>, <<"[::ffff:192.0.2.1]">>, 8080}, <<"http://[::ffff:192.0.2.1]:8080">>},
  2838. {{<<"http">>, <<"[::ffff:192.0.2.1]">>, 80}, <<"http://[::ffff:192.0.2.1]">>},
  2839. {make_ref(), <<"null">>},
  2840. {'*', <<$*>>}
  2841. ],
  2842. [{lists:flatten(io_lib:format("~p", [V])),
  2843. fun() -> R = iolist_to_binary(access_control_allow_origin(V)) end} || {V, R} <- Tests].
  2844. horse_access_control_allow_origin() ->
  2845. horse:repeat(200000,
  2846. access_control_allow_origin({<<"http">>, <<"example.org">>, 8080})
  2847. ).
  2848. -endif.
  2849. %% @doc Build the Access-Control-Expose-Headers header.
  2850. -spec access_control_expose_headers([binary()]) -> iodata().
  2851. access_control_expose_headers(Headers) ->
  2852. join_token_list(nonempty(Headers)).
  2853. -ifdef(TEST).
  2854. access_control_expose_headers_test_() ->
  2855. Tests = [
  2856. {[<<"accept">>], <<"accept">>},
  2857. {[<<"accept">>, <<"authorization">>, <<"content-type">>], <<"accept, authorization, content-type">>}
  2858. ],
  2859. [{lists:flatten(io_lib:format("~p", [V])),
  2860. fun() -> R = iolist_to_binary(access_control_expose_headers(V)) end} || {V, R} <- Tests].
  2861. access_control_expose_headers_error_test_() ->
  2862. Tests = [
  2863. []
  2864. ],
  2865. [{lists:flatten(io_lib:format("~p", [V])),
  2866. fun() -> {'EXIT', _} = (catch access_control_expose_headers(V)) end} || V <- Tests].
  2867. horse_access_control_expose_headers() ->
  2868. horse:repeat(200000,
  2869. access_control_expose_headers([<<"accept">>, <<"authorization">>, <<"content-type">>])
  2870. ).
  2871. -endif.
  2872. %% Internal.
  2873. %% Only return if the list is not empty.
  2874. nonempty(L) when L =/= [] -> L.
  2875. %% Parse a list of case sensitive tokens.
  2876. token_list(<<>>, Acc) -> lists:reverse(Acc);
  2877. token_list(<< C, R/bits >>, Acc) when ?IS_WS_COMMA(C) -> token_list(R, Acc);
  2878. token_list(<< C, R/bits >>, Acc) when ?IS_TOKEN(C) -> token(R, Acc, << C >>).
  2879. token(<< C, R/bits >>, Acc, T) when ?IS_TOKEN(C) -> token(R, Acc, << T/binary, C >>);
  2880. token(R, Acc, T) -> token_list_sep(R, [T|Acc]).
  2881. token_list_sep(<<>>, Acc) -> lists:reverse(Acc);
  2882. token_list_sep(<< C, R/bits >>, Acc) when ?IS_WS(C) -> token_list_sep(R, Acc);
  2883. token_list_sep(<< $,, R/bits >>, Acc) -> token_list(R, Acc).
  2884. %% Parse a list of case insensitive tokens.
  2885. token_ci_list(<<>>, Acc) -> lists:reverse(Acc);
  2886. token_ci_list(<< C, R/bits >>, Acc) when ?IS_WS_COMMA(C) -> token_ci_list(R, Acc);
  2887. token_ci_list(<< C, R/bits >>, Acc) when ?IS_TOKEN(C) -> ?LOWER(token_ci, R, Acc, <<>>).
  2888. token_ci(<< C, R/bits >>, Acc, T) when ?IS_TOKEN(C) -> ?LOWER(token_ci, R, Acc, T);
  2889. token_ci(R, Acc, T) -> token_ci_list_sep(R, [T|Acc]).
  2890. token_ci_list_sep(<<>>, Acc) -> lists:reverse(Acc);
  2891. token_ci_list_sep(<< C, R/bits >>, Acc) when ?IS_WS(C) -> token_ci_list_sep(R, Acc);
  2892. token_ci_list_sep(<< $,, R/bits >>, Acc) -> token_ci_list(R, Acc).
  2893. join_token_list([]) -> [];
  2894. join_token_list([H|T]) -> join_token_list(T, [H]).
  2895. join_token_list([], Acc) -> lists:reverse(Acc);
  2896. join_token_list([H|T], Acc) -> join_token_list(T, [H,<<", ">>|Acc]).