From cbae9570224164cf0c0c5d54d3538e1639148b3a Mon Sep 17 00:00:00 2001 From: Francesco Cozzuto Date: Sat, 6 Dec 2025 12:10:37 +0100 Subject: [PATCH] Increase the number of passing tests --- examples/001_serialize.c | 2 +- examples/003_references.c | 2 +- tests/test.c | 17 +++- tests/wpt/test | Bin 110448 -> 0 bytes url.c | 163 ++++++++++++++++++++++++++------------ url.h | 5 +- 6 files changed, 133 insertions(+), 56 deletions(-) delete mode 100644 tests/wpt/test diff --git a/examples/001_serialize.c b/examples/001_serialize.c index 7bf0745..d082cda 100644 --- a/examples/001_serialize.c +++ b/examples/001_serialize.c @@ -18,7 +18,7 @@ int main(void) } char buf[1<<9]; - ret = url_serialize(&parsed_url, NULL, buf, sizeof(buf)); + ret = url_serialize(parsed_url, NULL, buf, sizeof(buf)); // On error, url_serialize returns -1. Errors can // only occur when relative references are involved, diff --git a/examples/003_references.c b/examples/003_references.c index 9896855..3cc1d9a 100644 --- a/examples/003_references.c +++ b/examples/003_references.c @@ -36,7 +36,7 @@ int main(void) // Now we can resolve the reference by serializing it with // the base URL char buf[1<<9]; - ret = url_serialize(&parsed_relative_reference, &parsed_base_url, buf, sizeof(buf)); + ret = url_serialize(parsed_relative_reference, &parsed_base_url, buf, sizeof(buf)); // Since url_serialize was called with a non-NULL base URL // argument, it may fail. We need to check for a negative diff --git a/tests/test.c b/tests/test.c index a8e7a66..82bfcbe 100644 --- a/tests/test.c +++ b/tests/test.c @@ -314,7 +314,22 @@ static int run_test(cJSON *json) char buf[1<<10]; URL parsed; if (cJSON_IsNull(json_base)) { + + ret = url_remove_white_space(src.ptr, src.len, tmp, (int) sizeof(tmp)); + if (ret > (int) sizeof(tmp)) { + assert(0); // TODO + } + src.ptr = tmp; + src.len = ret; + ret = url_parse(src.ptr, src.len, NULL, &parsed, 0); + + if (ret > -1) + ret = url_serialize(parsed, NULL, buf, (int) sizeof(buf)); + + if (ret > -1) + ret = url_parse(buf, ret, NULL, &parsed, 0); + } else { if (!cJSON_IsString(json_base)) { test_printf(" JSON object field \"base\" is not a string or null:\n"); @@ -347,7 +362,7 @@ static int run_test(cJSON *json) ret = url_parse(src.ptr, src.len, NULL, &reference, URL_FLAG_ALLOWREF); if (ret > -1) - ret = url_serialize(&reference, &parsed_base, buf, (int) sizeof(buf)); + ret = url_serialize(reference, &parsed_base, buf, (int) sizeof(buf)); if (ret > -1) ret = url_parse(buf, ret, NULL, &parsed, 0); diff --git a/tests/wpt/test b/tests/wpt/test deleted file mode 100644 index c37ebe1a14037a6eb3bb437c15d3039362e4858b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 110448 zcmeEve|(eG_5YK!DWwGxglbik$PeX*LQ&N6OM(&&4zwtBs30xS0u$QWCj3wf#k5Km zt3^donX3*r+)(>*g2+U}4@q(A>d#eWGdi{<3)2pnR%PbK=_7)T!YBrIkd=|Qu>16oy zV}ehII^8I*MbNb*SU*JIb*Sbe+q4}N^tQu^aJH;>Xt2QR`N&+9FC_DYWIi2sNJHpQ zw=(4k)5 zOyncGoaoOc8LyG$rE7-*S#KRm^J=4)E}4JP*wITDp0{*Kg=gh?D+@0=@1n7zYAQ#a z&yqhj{2m@?;k>(r4k0c*q|0rnN}e1`%#B{^&RCjJpTCRrcnxJ3Ah{ed5#OdXGEe z*SnEmP_BQ%{Ygq%za5zdUy}xZ4HUBLnH6|b8vgsz;P<4#|1k}|EDip)H25ECAXGo%Sx9ntz4j$*OZmr3|eVTOjUU}(~rAQ$bEtps1E(K;; z>5>Yq#$CN&S(R2^RlTIbU9Oe7E0<{Hl~rXGTKR&dl{IC|P_>GHyK_a%QdPb#n+3pE2dAo^s;&9kGj~n8#C6pjo)1mzde7{h1$Gnvu4bLE@g|B)VRy4 zXU({5X=O#(tkU^Qsl-LgDl4Q3=Ly!7jA$)0Vy+qS)tvNSW>T7XEWl+Y;MIH(#yls% znaDrrcgm6_n4x5810?Q&^$#M9TeP!fxN zzip;L2ETSWZ_x225}%#qhk82jxyVOjiaw4ICSUSXaVox5;aA6}HU&@Pnf_~6@F(j? zz>tC;qTp>xxhg)a@Sm>m+ZBEl-=Xl^75+{Iul9324p_i@mcp<1U&0Sn@Zrxyq6dI% z5=L|=cpAg?U#Eg6S;kN5LvpAM^k24uCz}4VDR{k&iI%V6;V8z>uHbVFEanOdK3BmP zDEM9qzEHu-sWwv`3cioR?^5tq1z)7#Z3=$2g72%~OBB2sXBR7Yxn$v7RSI6ZKI2y^ z_&h~^or0&aOaHA?@cB9tut~w6s^B*&_|p`8vw~MAV_OtF%^mb#i-JE>M*{9p@BlJ7uGYsad{7CyZn{|A8VScA>cO0+Fag!*?U=B?84{%tZ7GLLw^fk9jFoi zVxk<0d=iVrnmA2???~h=BTWOsk;rZ%O#$#oi0^X5Gsgb5YcO){?NK=425}9J8DX<-hj4{#_(2hjTGSU>tjzszzX$oLR zA|@kk<@CS4)9XtC3-veB6sS;tBTWGc^*7QKm{5NsO#un@H_{Y{P=6y$10?Eiq$wbw z{zjSt5$bQGDFC7VMw$W->aVBKdhcPKXB{|I7?|NN$H;@r8gv{823%NJ=}C(vy3 zQPzR=a0+zr#z|u!%hhNZL10{`I}crdKbWZN_l$+C1M7%yuY}+?G5>|%5YO=MT)qz1 z?$0K>c6VmDO#5ALedq2E0Z&Q+*|AVL`eRS zA`xEopI9us5S?wm!UMUA}Fz8EhzF(6|_2 zy04*%LE}n*`heSR9qz2N4xiCv9bVjQ9X_|kI=n1k9los1I@}Yo4zKN4=<;U{9mZlC zT*xyf5Ns#_q(BpF7>m0CA~rg0E?+|-lCH*vA_Be!2Y}yl;kO`f+^N|~v-e%Y_xmk_ z80BirI~A33HBPj-8aI$km%l-1{+7$PCw%N!Eaq!CmxSbXe4}Z8zBje`ET1CP1ZL7_ zp=+ej&us~RgB%C;8s_jGw!3_tF5kagzSmqnXUOF%4)1q}exsoo8!g{GO!^0RP|omA5kH8-w;zEsEo)z!3SbcC0+|6#P{?JG%{~h=@)<=X+cIX|b63%Y};Y{oH08x5hDk$03mjhhm zb5udAAqX`#d7Z~herI@`^O*JSUqZ*Grg&3ai*dBj<*RLX`8;iChE|xp&EkPZRwPDR|10_u8SMJlahJdHX4zzPDdLE58L5lytHw4SpwTE=?&9q=V; zuh@f%(Y}Ny=+&}NDbyN%7!|-<+bMeShiZ;}l%vtos^VG*N9>*lKzddsZ2&33qY8A7 zR$`>Hb4wTVH_N)tkX0I&RUl>6#bwQo%eqyS^-D!ojv*^AF3Tll)yHM^ zipyH9%DO?3^)ljxsN(@lhFy)tAt~qXxSV}xcxkHyIVUM{mK$=`k{n^Hs;)Y%jLS-> z-~P_HtwtEKE;M8{)rk&Bre7OZZFpQSg+BO72?oH&`qU8#z@(-23`NELp#tcjC8-Gps^maCW5M1_dr z5FXXy$VOD$U@q=-wYXD^;t;y^;@GC~;-raD9LY6`qXa^fx3kCU(_xfes@TAn(kRhO zD2gp;bNOe4Fq`Rc`MB3!{BKM@K-jo{s-*+oN`zqe7GQle>a>;nik5QFMyKz@T%;)y)`5y&WdZ{BEzSjftQb zMBIi#Tx|GRR1HQtZG(b)lyLqTHh(bfAhsT}VyMIIE9XDnv|L@qS6{IJyprq3LPE(Vv3#8j(aiC8`d8q zbfj|q8$_|Xj*qH*J>xod3LR;S6>U8mI+jQs*C47T+wr4&mD2Wp!8+cmN`97b{u#6V z#U*ha2dW&6%+W~Y`ZtJTbsfJ#-!$q{20pT*^@ZZwtuHjtwthL>dzImLSF5^QEOd#f z5(WcjeZh&`=$5*@K2teN%)zn+lXSWRD$~IuMuXbfN*jhT*j2}-+s5HYbdTu>wQMqL z{T(10s&2kVso33+ho-Go%UDS`elO(<(!(#+(2ciK6Mv~~?zyb{;k5c(I2|m)pzd@*> z>zt+Xxxptpjzj3|5;`{vowvkwK7?T}mCh4YoregW&r{2=30+8{e*+6NbiP&Xe&G(T z8|mTSAk@%xzDDH>fRCMYyAvxqHL9*X+>FgPET$Jj*Df=>2Et;|^dt-8Ma*+ch`nJY z5xlzyXzqRpbd12D@vjD6%}jyotWs5&M5;DgrrfPG*Kk%}6oRZjMRykyim$N&sLSMa zOG6WtCre>WF#PF1Y30085Tv44#1-u+6s;4A63{%P)ksyVUwzJ6{RS&dqetIQIKQFQ zQFK0`)dHavON6>lVM7HgrLk~Rp#$sKxPd0u(|m0&qVcqybb;jXJ7;T}ufYvcQ*7;e2YS$a8wNfMcg-l_vW}y?8A`SLGhsj`DK=5y)D>5)w zOb_VW$FBXuc4zQWcOX1jr7+brZJxMlgSlIBfXtAJN3ZcH5!3wB5?ls6Tn7 ze;c)(xtr%hUhM;dw*lzHyo+BCbp zs?t=b1l&t%%quX|w9-mTinBJ%K-W;6t)D9BTQn{N{ZReW8{*Al+Y=4%mOU3_)$wUDm`d>zJDJ74qpYU67* zUp2mVqRC?0I`|sqYlyGyd~M@vD_;YA-N9G#8(95jj@#7Nej*}xcmYk~*6yRR2fJ54 z0_DeQY@hLuG_7!%XtKgPl~%BWS+qh-bhYIeMGU7lrxVplghtChRp$315CxsI2=!X& zOrC4NwqIp?25i7Rqej<91nx1F?@q$$?hp+B9es`!jb1NA$J`e~%*|?+Ymvpzp`KR; zx2Z*344TMK8*F3MYy*fyio255SUG=BT4RYF8Qw0*o5#|{$P7MXd(o34?AgHro#CBu zTkU21@o2PySb0hj44*Etbx@jPp}{p%aCNx60TYd3yZ_1sh>a%C$-00m|0e4c@ruG^ zhW)5W7))6EVNrJEWv&~<{DZ{E!r#O_ya#JHn)n98k3kmF14XuA_!5QpQ``&NovE|` zfyu?BdndL8_LK3Pi5sF0*m)8UFDOBl(t>3TECoGlJ2cO^F2Civy=?a|c0tZ&W?B|= zVc{qhi=GpU%%ljxi_~Cv^fa=h+;qd5&Tr`#7ojDI*dl7di|VC^ba7@O4(5SPZs%S^ za(=(ZP$k4;LtK>-Ma~dI&Q2IM=xn974LV;JpiO{x1!yP0HWFKmp@RoIZk~Wt5Bcwptts*$$c4Jt@mOS)^Hbd&&O4>HmpVZl^k5culxnB?|hg2 z{yKGFyAIlVovkKxo7#4hJ4diLDeO~K_H&s1byIYNU|ggy7OITjf#7wvnWCm9zw@Ba z+3t0|i>-mY^4*vbTbe&XJvNF3gmAw`%iW+*t)E8`$i+tB{nHSjR|ckVI~N=|CD_yomtu8piO4%Zup81%4avx!Sv9E zc1O@srZ#^u60dQuGQCERam?ar#IXs^gSWre)HFB#P^il$3|g9`<9UQ{Mmzc?qDOoa z<}$rJJbX|e6xpyuZj;ZAT2Wx3v2D{g{v71?-*eQ87w+p^=R8(dd#q%27OuCh?uDyk zb#Gj6Skr@fqZaO3U9*g4wsFU2Bgx}d=i@3$DOXCl{CL^Va!*9YA?}NCJ=pAWb zxDImI#H-gd zD#dMQaoD$0s-IpL>P4upo_W>x%DhEnK3$ePQ6#Am#&Z=MCt8I^MHQ&wMxSV&MIsNC zHkwPDe4;te`#w2o5D)u2htMb6(I;`@rnX&kXQQjP`*rW~IgcXU7KFc1Z0{rxIUn}- zJC9Cq9&%&z^r#3AVX+}jk1m5@dZq{4%L(gQfNe^B9q> z+l#;0L36T^6o^_~OAah5FE4lPj@Wm9ZQk8E(6`^$I=o}tp4GB2nsZSeU)%1G+0{Uh7B zai51AU-9#P95C2e?wjRo^VU9}?Y;rR#yK}rheq5(rA1XU{m|&!A_e@c` z`^v7RdoD6c7EG(zdhGqubRx|C4bRFbWZX=#z|<6U(xC$_I>7II$+gk?!gM@V_B)&W z#V@%Qyj||=RpfQPgrgfTO>jPcJ0@5GW08w?|z&yRHO z80QS!{>PwmALke*2|FmV?K9CulorxOa8dhE)V>MM9k;i!gCwu%F8Uy0Ade3Pz8RsZ zKIglTg<*h#Fy^G5h0)A-7q7i8xuZIWOvUsLEfQZRfb+C@D_ zA*Z+C2+67))7F@+x!z^ROsF8P-r8f@>WN*H%W;3&1=rL4W*1y<_f8hxwGzpLEou=; z?&s3tM8f7<(IAzGv~%)8eCk4Bs$;zLgjZ)A$lO06*z6XbrVCyh3<- z?3yB@9~qHnT>3%16WcBELH&+yWKBMvj zxJ|8ri8b&%Yw)^HNx_SA5j}&>I?^F}jo-OWk=5ACj0_{4>wE}#!XiFQI{6OABaW-+ zrF$3^7=4(@j+JAwYd*X@ddC&aT%nze7nQ_P*b1t0dMUC zJODosar8=1g`NYaDT&?=1&IgxI^6i28@nyCjUG&*NP3dvb8hn1ZsH<0Mc&9&icIa4 z4tFjFS=c{v1xhunnlB88(HG;j=yL3&8*$8MB>3Zg>axpBzM^4%=f>m=@al~!cY(oO zcwA@6#gQvMlL{N?E%>NtP^B2*#|~>l9Z1Km0Q52)(s~cApVAh47*F-m^=!-meucd8k}^nAcRlkMU4SjG}Orh#_Ms+%TRRnRVkFj;`!p^n)^F!`9^JVHKC;zr480* z#nk#h(yVqlm(1iVDl)P!CPT_pdC;*m!IrCpX%)Gmk0A)&M5N<+#R#QVLs~%MN1QQR<_c@h5eRJ=*63UXliBAS%1n^wKj}`ZKUC> z4bgZD`5pPRM?g_TO7=S(G)&UG$-pcoo^5eV&><%Q%@p1@bs@-%LC61ce3fz#H(e-0 z*C*(}noUVLl$7u|qkPpFdHnZGF|3w))Eey`7%#O-ksNfaq*Sy50%$qiT|b^f&phD= zOK@Pr=X@T^Y3|3*M;^ps1gvru|JVCfi`uWMqzb;Gl}4jYKx~$1zhfme(Qv{vTAHDy z%O_q>@o$(-ogk?@WPr?g;XATRxcnQ6h}qxBG|@GFjc0%)`)Tw>$et`@(>pI~c3E4;ZL87(c-)JJ}y+O#f z3)#a`$hJUl#wW`bc^a7}WIrfnKP+TV7P4(Zc76)kX2@oIvTTv3k!eEqc<}&q%Jjwy z|4~7)?3-i*XH3JX+5|z9G9<1_aXQ#?5(?wD_B>D z6#Zn5AfI9;gwZp^378ZyW#VLO16kp?ar!qn=fnMEM>7F5^Nr&=qK2R(W~lPR|j2W%Gb0mo|kfNVa%Z(y^BDEZRg0*}V!!_43 z&}}VWqUY+U<&7|k&$-WAyN_$RFG3pooq-g!#6EhwmQ)p^mQ)R6b==3#Q-a0tL7y#} zexzpyfCjf=l4P{9zO61E&S7oes-E<&-w&nN?rTMHH%^%|W$u)lrp!y&^;Y*-u!}X_ zNBeKJZM3=IFK(AxB{&*RHUEHXj?JxO+A0h7aiXVSSAkCVW9lS#C4?5rPA$Jzg@Xhg zF7ye0)h&QMsn(z~1eIX6RvhT3L(u^`T0h!z9tgtEVNgE6;0%BXiwf+hGPiz2;v%pG zori>!LwG-ncH0hN|E?9=F||0`Sd5H^uzf3+v7S$m9O(@EoQJ)&hc$hh{&NTryf{Y> zUYri-@H-FVi~zR8yq#tb4of*ZNV!oSdNT>94+40;LdlUxV!IEOxAA^ofn3C=C z&9p^nYDaZ-pDpxP>guNbIl~z4?8t}M2cm6t9#OisKR;`aw-AhglCeeR=Ql6HepfGF z+wfN2*Th!ec-l&0`%G{iTa6XXF*;EJPkR*x#y8)@^Qcz5*wAnQyB%@H>{Po(5`w82N%o6k{0&s5jJ)$O|MxZY0;_iyUA0xYeojU%Cn^30Djm}m8wqpb zENnh*f(-+R6+eH&FxF|AtlqNz;Dx!3M_r6SzcYOb|H~BK-|yH|q88 za?Iey{l9FEnbgYE9E}?SD2bJQaSBBk-ygql{iaJR5tDSTzw@I%{YdWJ77XAGnIMyk zMsOCGLfo?p*d8sy9y`+5gYkHkOyNplk34J-`nd#s7v@m!!s-lJ&L zxWPs?6(J>7wK!H)604dWt160Bxnfn0SXE)HsvuT1ELLTYRprO3Y-pMABlLDEn$6!p zy@&kcVh#lymt1~V6>PCG(H8Hcx%4Wp2F)^HKDSMisP4uz)n&ZDLFGzO-Bl3YU3ISq zJ@PkhClNa3+n?5Z-Xdh9<=#?OrcNlcE{!sb#~T(3R|;hgV+YRYt8zIa79t7G@haFN z*PwXah}_51^@9eAR$^9res{g%E%A8D_a-`km_b%6)uyK%9*0!UPdf&lZl!spv^qoD zkhnCZot17`#98!i`ER&|aDB|ot>TWR-`pFzs|UUI7e8?S48#N*vS9)!V)X8}=5eD{ ziRM_DrqLLWof(BIMWgZCuUx_j_wG#EcjPQa?i^Wk)~w*NfQv)#yYuMTWY$ zWIlT7VL3KCF@@l=!%Mh6mifTN)ukmzJ{|m`BfL0tgfD#b0Kn-Dd17)6N)NjxI76Pn zq6!s+K)0@^H7^R(qI0lhz`N~UtXw4xFY96AvgHL#+V%n!0=qzM0BYP`_e%+UbzS+E z5o(XGeK=;U>iPiM+6`@k&JHTyC65@g_PS0psRWum)oe#AiiLCssp9h60I=4>nFvho zTyTZV{~N2D$hsY-wd z9X|)dV1b&>4CD*^0i!-#ARXwooX-@?NBjb-tY$bdGu4CuYn&KNg|8FEw<{1YVcG9@-qaP7+`|{gx#m_@g8}bD^I3U3w=^6j-QcE`-7$IZaoj6$9SOBTUJcA!VKs0$)|e5Bm^dEkh(FRAHlqmsfj;H`819_e;U1BM97 zHzH7Ai$-6Zq6H{9ioW?Xh{iOTC%RJ5$$(&U5o~vvTICUt?ReNT(%U)S`p};Gw>=&X z5{Q%UeCqPGxpw|_4ZT>XWxCGV<7#-@x`BAC-VphkbwdDsyl3=M?%C1a44@cA8LS*> zmw?q9D$2!7lFP-8AE^Nk9k<7N2TdEJ8GNEqpwV*pq=I)3X7zt$|H6h6z0M+E{Fw&n zLI8;)Ihdf*<_~{~1l^YVLW+&Nyd>2LXbVDuR3&LpTIR;EJU7 z4uolE7%l85;Jb3`Xv-5fXj*k|A_g6_8^0!cE`|2{fd<(bHfRu07qv+V=eAJ}oEQ)p z^Rdd6c40fWxkjRV-a~4^Cb_hgsuqt0Nv~wB_#gG46NNEy#ttg7gH-+MdZ}t>f~sMl zN!1Qkm1uEQk(pKP6spoVMFL>}D4slOCG?R3l;4g%(uPJ0Lm(EEbd;Q4l4?hc@P;t) z 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 'A' && c <= 'Z') + return c - 'A' + 'a'; + return c; +} + +static URL_b32 streqcase(URL_String a, URL_String b) +{ + if (a.len != b.len) + return 0; + for (int i = 0; i < a.len; i++) + if (to_lower(a.ptr[i]) != to_lower(b.ptr[i])) + return 0; + return 1; +} + static URL_b32 is_alpha(char c) { return (c >= 'a' && c <= 'z') @@ -342,10 +359,22 @@ int url_parse_ipv6(char *src, int len, int *pcur, URL_IPv6 *out) return 0; } +static URL_b32 is_special_scheme(URL_String scheme) +{ + return streqcase(scheme, S("ftp")) + || streqcase(scheme, S("file")) + || streqcase(scheme, S("http")) + || streqcase(scheme, S("https")) + || streqcase(scheme, S("ws")) + || streqcase(scheme, S("wss")); +} + int url_parse(char *src, int len, int *pcur, URL *out, int flags) { int cur = pcur ? *pcur : 0; + out->flags = flags; + // Parse scheme characters until the end of the string, // a character not allowed in the scheme, or ':'. If the // found character wasn't ':', set the scheme to empty @@ -385,13 +414,30 @@ int url_parse(char *src, int len, int *pcur, URL *out, int flags) // info or the domain until we hit a @ character or // a character not allowed in the user info, se we // need to backtrack in a similar way to the scheme + int authority_off = cur; out->no_authority = 1; out->no_userinfo = 1; - int authority_off = cur; if (len - cur > 1 && src[cur] == '/' && src[cur+1] == '/') { cur += 2; // Consume the // out->no_authority = 0; + } else { + // The WHATWG standard has a special case for special + // schemes (ftp, http, https, ...) where if a scheme + // if followed by a single "/", it behaves like "//". + // + // TODO: This rule does not apply if there is a base + // URL with a proper authority. Unfortunately, + // we don't have access to base URL information + // at this point. + if ((flags & URL_FLAG_RFC3986) == 0) { + if (is_special_scheme(out->scheme) && !streqcase(out->scheme, S("file")) && cur < len && src[cur] == '/') { + cur++; + out->no_authority = 0; + } + } + } + if (!out->no_authority) { out->username = EMPTY; out->password = EMPTY; if (cur < len && (is_userinfo(src[cur]) || src[cur] == ':')) { @@ -512,7 +558,6 @@ int url_parse(char *src, int len, int *pcur, URL *out, int flags) // TODO: What if the dot is percent-encoded? if (src[cur-1] == '.') cur--; - } else { out->host_type = URL_HOST_EMPTY; } @@ -528,7 +573,7 @@ int url_parse(char *src, int len, int *pcur, URL *out, int flags) // The WHATWG standard forbids port numbers with the // "file" protocol. if ((flags & URL_FLAG_RFC3986) == 0) { - if (streq(out->scheme, S("file"))) + if (streqcase(out->scheme, S("file"))) return -1; } @@ -550,7 +595,7 @@ int url_parse(char *src, int len, int *pcur, URL *out, int flags) // part of the path and not the authority if userinfo, // host, and port are missing if ((flags & URL_FLAG_RFC3986) == 0) { - if ((streq(out->scheme, S("http")) || streq(out->scheme, S("https"))) + if ((streqcase(out->scheme, S("http")) || streqcase(out->scheme, S("https"))) && out->username.len == 0 && out->password.len == 0 && out->host_type == URL_HOST_EMPTY @@ -600,13 +645,7 @@ int url_parse(char *src, int len, int *pcur, URL *out, int flags) // For a subset of protocols, WHATWG sets the default // path to "/" if ((flags & URL_FLAG_RFC3986) == 0) { - if (out->path.len == 0 && ( - streq(out->scheme, S("ftp")) || - streq(out->scheme, S("file")) || - streq(out->scheme, S("http")) || - streq(out->scheme, S("https")) || - streq(out->scheme, S("ws")) || - streq(out->scheme, S("wss")))) + if (out->path.len == 0 && is_special_scheme(out->scheme)) out->path = S("/"); } @@ -675,19 +714,22 @@ int url_parse(char *src, int len, int *pcur, URL *out, int flags) typedef struct { int count; + URL_b32 first_slash; + URL_b32 trailing_slash; URL_String stack[PATH_COMPONENT_LIMIT]; } PathComps; static int resolve_dots_and_append_comps(PathComps *comps, URL_String src) { - URL_b32 is_absolute = 0; + if (src.len == 0) + return 0; + if (src.len > 0 && src.ptr[0] == '/') { - is_absolute = 1; + if (comps->count == 0) + comps->first_slash = 1; src.ptr++; src.len--; - if (src.len == 0) - return 0; } int i = 0; @@ -698,24 +740,16 @@ resolve_dots_and_append_comps(PathComps *comps, URL_String src) i++; int len = i - off; - if (len == 0) - return -1; // Empty component - URL_String comp = { src.ptr + off, len }; - if (comp.len == 2 && comp.ptr[0] == '.' && comp.ptr[1] == '.') { - if (comps->count == 0) { - // For absolute paths, ".." at root is ignored (stays at root) - // For relative paths, ".." with no components references parent, which is invalid - if (!is_absolute) - return -1; - // Otherwise, ignore the ".." (absolute path, already at root) - } else { + if (streq(comp, S(".."))) { + if (comps->count > 0) comps->count--; + } else { + if (!streq(comp, S("."))) { + if (comps->count == PATH_COMPONENT_LIMIT) + return -1; // To many components + comps->stack[comps->count++] = comp; } - } else if (comp.len != 1 || comp.ptr[0] != '.') { - if (comps->count == PATH_COMPONENT_LIMIT) - return -1; // To many components - comps->stack[comps->count++] = comp; } if (i == src.len) @@ -728,6 +762,7 @@ resolve_dots_and_append_comps(PathComps *comps, URL_String src) break; } + comps->trailing_slash = (src.len > 0 && src.ptr[src.len-1] == '/'); return 0; } @@ -808,17 +843,35 @@ static void append_fragment(Builder *b, URL_String fragment) } } -int url_serialize(URL *url, URL *base, char *dst, int cap) +int url_serialize(URL url, URL *base, char *dst, int cap) { if (base != NULL && base->scheme.len == 0) return -1; // Base is not an absolute URL + if ((url.flags & URL_FLAG_RFC3986) == 0) { + if (base + && streqcase(url.scheme, base->scheme) + && is_special_scheme(url.scheme) + && url.no_authority) { + url.scheme = EMPTY; + } + } + Builder b = { dst, cap, 0 }; - if (url->scheme.len > 0) { - append_scheme(&b, url->scheme); - append_authority(&b, *url); - append(&b, url->path); - append_query(&b, url->query); + if (url.scheme.len > 0) { + append_scheme(&b, url.scheme); + append_authority(&b, url); + PathComps comps = {0}; + if (resolve_dots_and_append_comps(&comps, url.path) < 0) + return -1; + for (int i = 0; i < comps.count; i++) { + if (i > 0 || comps.first_slash) + append(&b, S("/")); + append(&b, comps.stack[i]); + } + if (comps.trailing_slash) + append(&b, S("/")); + append_query(&b, url.query); } else { if (base == NULL) { // No base URL provided, which means the @@ -827,34 +880,37 @@ int url_serialize(URL *url, URL *base, char *dst, int cap) } ASSERT(base->scheme.len > 0); append_scheme(&b, base->scheme); - if (!url->no_authority) { - append_authority(&b, *url); + if (!url.no_authority) { + append_authority(&b, url); PathComps comps = {0}; - if (resolve_dots_and_append_comps(&comps, url->path) < 0) + if (resolve_dots_and_append_comps(&comps, url.path) < 0) return -1; for (int i = 0; i < comps.count; i++) { - append(&b, S("/")); + if (i > 0 || comps.first_slash) + append(&b, S("/")); append(&b, comps.stack[i]); } - append_query(&b, url->query); + if (comps.trailing_slash) + append(&b, S("/")); + append_query(&b, url.query); } else { append_authority(&b, *base); - if (url->path.len == 0) { + if (url.path.len == 0) { append(&b, base->path); - if (url->query.len > 0) { - append_query(&b, url->query); + if (url.query.len > 0) { + append_query(&b, url.query); } else { append_query(&b, base->query); } } else { - ASSERT(url->path.len > 0); + ASSERT(url.path.len > 0); PathComps comps = {0}; - if (url->path.ptr[0] == '/') { - if (resolve_dots_and_append_comps(&comps, url->path) < 0) + if (url.path.ptr[0] == '/') { + if (resolve_dots_and_append_comps(&comps, url.path) < 0) return -1; } else { if (!base->no_authority && base->path.len == 0) { - if (resolve_dots_and_append_comps(&comps, url->path) < 0) + if (resolve_dots_and_append_comps(&comps, url.path) < 0) return -1; } else { if (resolve_dots_and_append_comps(&comps, base->path) < 0) @@ -862,19 +918,22 @@ int url_serialize(URL *url, URL *base, char *dst, int cap) if (comps.count > 0) { comps.count--; } - if (resolve_dots_and_append_comps(&comps, url->path) < 0) + if (resolve_dots_and_append_comps(&comps, url.path) < 0) return -1; } } for (int i = 0; i < comps.count; i++) { - append(&b, S("/")); + if (i > 0 || comps.first_slash) + append(&b, S("/")); append(&b, comps.stack[i]); } - append_query(&b, url->query); + if (comps.trailing_slash) + append(&b, S("/")); + append_query(&b, url.query); } } } - append_fragment(&b, url->fragment); + append_fragment(&b, url.fragment); return b.len; } diff --git a/url.h b/url.h index 0ff1b56..fb65154 100644 --- a/url.h +++ b/url.h @@ -47,6 +47,9 @@ typedef enum { typedef struct { + // Flags used to parse this URL + int flags; + // These flags are used to differentiate between // an empty userinfo/authority and an undefined one. // For instance: @@ -169,6 +172,6 @@ int url_percent_decode(URL_String str, char *dst, int cap); // set to NULL. // Note that this can be used to normalize an URL, even // though it's just a best-effor normalization for now. -int url_serialize(URL *url, URL *base, char *dst, int cap); +int url_serialize(URL url, URL *base, char *dst, int cap); #endif // URL_INCLUDED