cow_http_hd.erl 132 KB

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