2
0
mirror of https://github.com/boostorg/url.git synced 2026-01-19 04:42:15 +00:00

docs: convert qbk content to asciidoc

This commit is contained in:
alandefreitas
2023-11-07 11:38:04 -03:00
committed by Alan de Freitas
parent e9f69ef7f0
commit 446b5343ff
46 changed files with 6951 additions and 2266 deletions

View File

@@ -0,0 +1,154 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.2" width="116.84mm" height="25.4mm" viewBox="0 0 11684 2540" preserveAspectRatio="xMidYMid" fill-rule="evenodd" stroke-width="28.222" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" xmlns:ooo="http://xml.openoffice.org/svg/export" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:presentation="http://sun.com/xmlns/staroffice/presentation" xmlns:smil="http://www.w3.org/2001/SMIL20/" xmlns:anim="urn:oasis:names:tc:opendocument:xmlns:animation:1.0" xml:space="preserve">
<defs class="ClipPathGroup">
<clipPath id="presentation_clip_path" clipPathUnits="userSpaceOnUse">
<rect x="0" y="0" width="11684" height="2540"/>
</clipPath>
<clipPath id="presentation_clip_path_shrink" clipPathUnits="userSpaceOnUse">
<rect x="11" y="2" width="11661" height="2535"/>
</clipPath>
</defs>
<defs>
<font id="EmbeddedFont_1" horiz-adv-x="2048">
<font-face font-family="Liberation Sans embedded" units-per-em="2048" font-weight="normal" font-style="normal" ascent="1852" descent="423"/>
<missing-glyph horiz-adv-x="2048" d="M 0,0 L 2047,0 2047,2047 0,2047 0,0 Z"/>
<glyph unicode="x" horiz-adv-x="980" d="M 801,0 L 510,444 217,0 23,0 408,556 41,1082 240,1082 510,661 778,1082 979,1082 612,558 1002,0 801,0 Z"/>
<glyph unicode="w" horiz-adv-x="1509" d="M 1174,0 L 965,0 776,765 740,934 C 734,904 725,861 712,805 699,748 631,480 508,0 L 300,0 -3,1082 175,1082 358,347 C 363,331 377,265 401,149 L 418,223 644,1082 837,1082 1026,339 1072,149 1103,288 1308,1082 1484,1082 1174,0 Z"/>
<glyph unicode="u" horiz-adv-x="874" d="M 314,1082 L 314,396 C 314,325 321,269 335,230 349,191 371,162 402,145 433,128 478,119 537,119 624,119 692,149 742,208 792,267 817,350 817,455 L 817,1082 997,1082 997,231 C 997,105 999,28 1003,0 L 833,0 C 832,3 832,12 831,27 830,42 830,59 829,78 828,97 826,132 825,185 L 822,185 C 781,110 733,58 679,27 624,-4 557,-20 476,-20 357,-20 271,10 216,69 161,128 133,225 133,361 L 133,1082 314,1082 Z"/>
<glyph unicode="t" horiz-adv-x="531" d="M 554,8 C 495,-8 434,-16 372,-16 228,-16 156,66 156,229 L 156,951 31,951 31,1082 163,1082 216,1324 336,1324 336,1082 536,1082 536,951 336,951 336,268 C 336,216 345,180 362,159 379,138 408,127 450,127 474,127 509,132 554,141 L 554,8 Z"/>
<glyph unicode="s" horiz-adv-x="927" d="M 950,299 C 950,197 912,118 835,63 758,8 650,-20 511,-20 376,-20 273,2 200,47 127,91 79,160 57,254 L 216,285 C 231,227 263,185 311,158 359,131 426,117 511,117 602,117 669,131 712,159 754,187 775,229 775,285 775,328 760,362 731,389 702,416 654,438 589,455 L 460,489 C 357,516 283,542 240,568 196,593 162,624 137,661 112,698 100,743 100,796 100,895 135,970 206,1022 276,1073 378,1099 513,1099 632,1099 727,1078 798,1036 868,994 912,927 931,834 L 769,814 C 759,862 732,899 689,925 645,950 586,963 513,963 432,963 372,951 333,926 294,901 275,864 275,814 275,783 283,758 299,738 315,718 339,701 370,687 401,673 467,654 568,629 663,605 732,583 774,563 816,542 849,520 874,495 898,470 917,442 930,410 943,377 950,340 950,299 Z"/>
<glyph unicode="r" horiz-adv-x="530" d="M 142,0 L 142,830 C 142,906 140,990 136,1082 L 306,1082 C 311,959 314,886 314,861 L 318,861 C 347,954 380,1017 417,1051 454,1085 507,1102 575,1102 599,1102 623,1099 648,1092 L 648,927 C 624,934 592,937 552,937 477,937 420,905 381,841 342,776 322,684 322,564 L 322,0 142,0 Z"/>
<glyph unicode="p" horiz-adv-x="927" d="M 1053,546 C 1053,169 920,-20 655,-20 488,-20 376,43 319,168 L 314,168 C 317,163 318,106 318,-2 L 318,-425 138,-425 138,861 C 138,972 136,1046 132,1082 L 306,1082 C 307,1079 308,1070 309,1054 310,1037 312,1012 314,978 315,944 316,921 316,908 L 320,908 C 352,975 394,1024 447,1055 500,1086 569,1101 655,1101 788,1101 888,1056 954,967 1020,878 1053,737 1053,546 Z M 864,542 C 864,693 844,800 803,865 762,930 698,962 609,962 538,962 482,947 442,917 401,887 371,840 350,777 329,713 318,630 318,528 318,386 341,281 386,214 431,147 505,113 607,113 696,113 762,146 803,212 844,277 864,387 864,542 Z"/>
<glyph unicode="o" horiz-adv-x="980" d="M 1053,542 C 1053,353 1011,212 928,119 845,26 724,-20 565,-20 407,-20 288,28 207,125 126,221 86,360 86,542 86,915 248,1102 571,1102 736,1102 858,1057 936,966 1014,875 1053,733 1053,542 Z M 864,542 C 864,691 842,800 798,868 753,935 679,969 574,969 469,969 393,935 346,866 299,797 275,689 275,542 275,399 298,292 345,221 391,149 464,113 563,113 671,113 748,148 795,217 841,286 864,395 864,542 Z"/>
<glyph unicode="m" horiz-adv-x="1457" d="M 768,0 L 768,686 C 768,791 754,863 725,903 696,943 645,963 570,963 493,963 433,934 388,875 343,816 321,734 321,627 L 321,0 142,0 142,851 C 142,977 140,1054 136,1082 L 306,1082 C 307,1079 307,1070 308,1055 309,1040 310,1024 311,1005 312,986 313,950 314,897 L 317,897 C 356,974 400,1027 450,1057 500,1087 561,1102 633,1102 715,1102 780,1086 828,1053 875,1020 908,968 927,897 L 930,897 C 967,970 1013,1022 1066,1054 1119,1086 1183,1102 1258,1102 1367,1102 1447,1072 1497,1013 1546,954 1571,856 1571,721 L 1571,0 1393,0 1393,686 C 1393,791 1379,863 1350,903 1321,943 1270,963 1195,963 1116,963 1055,934 1012,876 968,817 946,734 946,627 L 946,0 768,0 Z"/>
<glyph unicode="l" horiz-adv-x="187" d="M 138,0 L 138,1484 318,1484 318,0 138,0 Z"/>
<glyph unicode="h" horiz-adv-x="874" d="M 317,897 C 356,968 402,1020 457,1053 511,1086 580,1102 663,1102 780,1102 867,1073 923,1015 978,956 1006,858 1006,721 L 1006,0 825,0 825,686 C 825,762 818,819 804,856 790,893 767,920 735,937 703,954 659,963 602,963 517,963 450,934 399,875 348,816 322,737 322,638 L 322,0 142,0 142,1484 322,1484 322,1098 C 322,1057 321,1015 319,972 316,929 315,904 314,897 L 317,897 Z"/>
<glyph unicode="e" horiz-adv-x="980" d="M 276,503 C 276,379 302,283 353,216 404,149 479,115 578,115 656,115 719,131 766,162 813,193 844,233 861,281 L 1019,236 C 954,65 807,-20 578,-20 418,-20 296,28 213,123 129,218 87,360 87,548 87,727 129,864 213,959 296,1054 416,1102 571,1102 889,1102 1048,910 1048,527 L 1048,503 276,503 Z M 862,641 C 852,755 823,838 775,891 727,943 658,969 568,969 481,969 412,940 361,882 310,823 282,743 278,641 L 862,641 Z"/>
<glyph unicode="c" horiz-adv-x="901" d="M 275,546 C 275,402 298,295 343,226 388,157 457,122 548,122 612,122 666,139 709,174 752,209 778,262 788,334 L 970,322 C 956,218 912,135 837,73 762,11 668,-20 553,-20 402,-20 286,28 207,124 127,219 87,359 87,542 87,724 127,863 207,959 287,1054 402,1102 551,1102 662,1102 754,1073 827,1016 900,959 945,880 964,779 L 779,765 C 770,825 746,873 708,908 670,943 616,961 546,961 451,961 382,929 339,866 296,803 275,696 275,546 Z"/>
<glyph unicode="a" horiz-adv-x="1060" d="M 414,-20 C 305,-20 224,9 169,66 114,123 87,202 87,302 87,414 124,500 198,560 271,620 390,652 554,656 L 797,660 797,719 C 797,807 778,870 741,908 704,946 645,965 565,965 484,965 426,951 389,924 352,897 330,853 323,793 L 135,810 C 166,1005 310,1102 569,1102 705,1102 807,1071 876,1009 945,946 979,856 979,738 L 979,272 C 979,219 986,179 1000,152 1014,125 1041,111 1080,111 1097,111 1117,113 1139,118 L 1139,6 C 1094,-5 1047,-10 1000,-10 933,-10 885,8 855,43 824,78 807,132 803,207 L 797,207 C 751,124 698,66 637,32 576,-3 501,-20 414,-20 Z M 455,115 C 521,115 580,130 631,160 682,190 723,231 753,284 782,336 797,390 797,445 L 797,534 600,530 C 515,529 451,520 408,504 364,488 330,463 307,430 284,397 272,353 272,299 272,240 288,195 320,163 351,131 396,115 455,115 Z"/>
<glyph unicode="@" horiz-adv-x="1747" d="M 1902,755 C 1902,631 1883,519 1845,419 1806,318 1753,241 1685,186 1616,131 1540,104 1455,104 1389,104 1338,119 1302,148 1266,177 1248,221 1248,280 L 1251,350 1245,350 C 1201,268 1147,207 1082,166 1017,125 946,104 871,104 766,104 685,138 628,206 570,274 541,368 541,489 541,598 563,700 606,794 649,888 709,963 786,1018 863,1073 949,1101 1043,1101 1189,1101 1289,1040 1344,919 L 1350,919 1389,1079 1545,1079 1429,573 C 1404,464 1392,379 1392,320 1392,257 1419,226 1473,226 1526,226 1576,249 1621,295 1666,341 1701,404 1727,485 1753,566 1766,655 1766,753 1766,872 1740,978 1689,1071 1638,1163 1564,1234 1467,1284 1370,1333 1257,1358 1128,1358 967,1358 824,1322 700,1251 576,1180 479,1077 408,943 337,808 302,658 302,491 302,362 328,249 381,151 433,52 509,-23 608,-76 707,-129 822,-155 954,-155 1051,-155 1149,-142 1248,-117 1347,-92 1450,-51 1557,7 L 1612,-105 C 1515,-163 1411,-207 1298,-237 1185,-268 1070,-283 954,-283 793,-283 653,-251 533,-187 412,-124 320,-33 257,85 193,202 161,338 161,491 161,678 203,847 286,1000 369,1153 484,1272 631,1357 778,1442 943,1484 1126,1484 1287,1484 1425,1454 1542,1394 1659,1333 1748,1248 1810,1138 1871,1028 1902,900 1902,755 Z M 1296,747 C 1296,815 1274,870 1230,912 1186,953 1127,974 1054,974 987,974 927,953 875,911 822,868 781,810 751,735 721,660 706,578 706,491 706,411 722,348 754,303 785,258 834,235 900,235 983,235 1060,270 1129,340 1198,410 1246,497 1273,602 1288,663 1296,712 1296,747 Z"/>
<glyph unicode=":" horiz-adv-x="239" d="M 187,875 L 187,1082 382,1082 382,875 187,875 Z M 187,0 L 187,207 382,207 382,0 187,0 Z"/>
<glyph unicode="8" horiz-adv-x="980" d="M 1050,393 C 1050,263 1009,162 926,89 843,16 725,-20 570,-20 419,-20 302,16 217,87 132,158 89,260 89,391 89,483 115,560 168,623 221,686 288,724 370,737 L 370,741 C 293,759 233,798 189,858 144,918 122,988 122,1069 122,1176 162,1263 243,1330 323,1397 431,1430 566,1430 705,1430 814,1397 895,1332 975,1267 1015,1178 1015,1067 1015,986 993,916 948,856 903,796 842,758 765,743 L 765,739 C 855,724 925,686 975,625 1025,563 1050,486 1050,393 Z M 828,1057 C 828,1216 741,1296 566,1296 481,1296 417,1276 373,1236 328,1196 306,1136 306,1057 306,976 329,915 375,873 420,830 485,809 568,809 653,809 717,829 762,868 806,907 828,970 828,1057 Z M 863,410 C 863,497 837,563 785,608 733,652 660,674 566,674 475,674 403,650 352,603 301,555 275,489 275,406 275,212 374,115 572,115 670,115 743,139 791,186 839,233 863,307 863,410 Z"/>
<glyph unicode="0" horiz-adv-x="980" d="M 1059,705 C 1059,470 1018,290 935,166 852,42 729,-20 567,-20 405,-20 283,42 202,165 121,288 80,468 80,705 80,947 120,1128 199,1249 278,1370 402,1430 573,1430 739,1430 862,1369 941,1247 1020,1125 1059,944 1059,705 Z M 876,705 C 876,908 853,1056 806,1147 759,1238 681,1284 573,1284 462,1284 383,1239 335,1149 286,1059 262,911 262,705 262,505 287,359 336,266 385,173 462,127 569,127 675,127 753,174 802,269 851,364 876,509 876,705 Z"/>
<glyph unicode="/" horiz-adv-x="557" d="M 0,-20 L 411,1484 569,1484 162,-20 0,-20 Z"/>
<glyph unicode="." horiz-adv-x="239" d="M 187,0 L 187,219 382,219 382,0 187,0 Z"/>
</font>
</defs>
<defs>
<font id="EmbeddedFont_2" horiz-adv-x="2048">
<font-face font-family="Liberation Sans Narrow embedded" units-per-em="2048" font-weight="normal" font-style="italic" ascent="1482" descent="423"/>
<missing-glyph horiz-adv-x="2048" d="M 0,0 L 2047,0 2047,2047 0,2047 0,0 Z"/>
<glyph unicode="u" horiz-adv-x="848" d="M 339,1082 L 240,446 C 236,420 232,394 228,367 224,341 222,317 222,296 222,241 234,197 257,166 280,135 317,120 369,120 404,120 438,128 470,145 501,162 530,186 556,217 582,248 604,285 622,329 640,372 654,421 662,475 L 753,1082 894,1082 766,230 C 763,208 759,184 756,159 L 745,88 737,31 C 734,15 733,5 732,0 L 593,0 C 593,3 594,12 596,27 L 603,78 C 606,96 609,115 612,135 615,155 617,172 619,185 L 613,185 C 594,154 575,125 556,100 536,75 514,53 491,36 467,18 441,4 412,-5 383,-14 351,-19 314,-19 233,-19 173,5 132,54 91,103 71,173 71,265 71,289 73,316 78,346 83,376 87,403 91,428 L 192,1082 339,1082 Z"/>
<glyph unicode="t" horiz-adv-x="477" d="M 368,4 C 348,-2 326,-7 302,-12 277,-17 252,-20 226,-20 179,-20 143,-3 116,31 89,65 76,110 76,166 76,187 78,210 82,234 85,258 89,279 91,296 L 194,951 89,951 111,1082 214,1082 300,1324 398,1324 359,1082 523,1082 502,951 339,951 239,320 C 237,307 234,290 231,272 228,253 227,237 227,224 227,192 233,167 245,149 256,132 275,123 301,123 315,123 328,124 341,127 353,129 367,132 383,137 L 368,4 Z"/>
<glyph unicode="s" horiz-adv-x="821" d="M 744,317 C 744,260 735,211 716,169 697,126 671,91 637,63 603,35 562,14 513,1 464,-13 410,-20 349,-20 298,-20 254,-15 216,-4 177,7 144,22 117,43 89,63 66,88 48,119 29,149 15,184 4,223 L 125,279 C 133,252 144,229 157,208 170,187 185,169 204,155 223,140 245,129 271,122 297,115 327,111 362,111 397,111 429,115 458,122 487,129 513,140 534,155 555,170 571,190 583,214 594,238 600,267 600,301 600,328 595,351 585,370 574,389 560,405 541,420 522,434 499,447 471,459 444,470 414,483 381,497 346,511 312,526 280,543 248,560 220,580 196,603 171,626 152,654 138,686 123,717 116,754 116,797 116,852 126,898 145,937 164,975 191,1006 224,1030 257,1054 295,1072 339,1083 383,1094 430,1099 479,1099 524,1099 565,1094 602,1085 639,1076 672,1061 700,1041 728,1020 751,994 769,962 787,929 799,890 806,844 L 672,819 C 660,872 637,910 604,933 571,956 526,968 469,968 440,968 413,965 388,960 362,955 340,946 321,934 302,922 286,907 275,887 264,868 258,845 258,817 258,790 263,767 274,749 285,731 299,715 318,701 337,687 360,674 386,663 386,663 416,650 474,624 505,611 537,597 569,583 602,569 631,550 657,526 682,502 703,474 720,440 736,406 744,365 744,317 Z"/>
<glyph unicode="r" horiz-adv-x="610" d="M 589,938 C 580,941 569,944 556,947 543,950 529,951 515,951 480,951 449,939 421,914 393,889 369,858 348,820 327,782 309,740 294,694 279,649 269,605 262,564 L 175,0 28,0 157,830 C 161,853 165,877 168,900 L 178,968 188,1031 196,1082 335,1082 C 335,1067 335,1049 335,1029 L 325,969 316,910 C 313,891 311,874 309,861 L 310,861 C 327,902 343,938 360,969 377,999 394,1024 412,1044 430,1063 449,1078 470,1088 491,1097 513,1102 538,1102 544,1102 551,1102 558,1101 565,1100 572,1098 579,1097 586,1096 593,1094 600,1093 607,1091 612,1089 616,1088 L 589,938 Z"/>
<glyph unicode="p" horiz-adv-x="954" d="M 453,-20 C 386,-20 331,-3 288,32 246,67 216,115 198,178 L 194,178 C 194,177 193,171 192,159 190,148 188,133 185,116 L 177,59 C 174,38 171,18 168,-1 L 105,-425 -42,-425 150,862 C 154,888 157,913 161,937 165,961 168,983 171,1002 174,1022 176,1039 178,1052 180,1066 182,1076 183,1082 L 328,1082 C 328,1077 327,1068 325,1056 324,1044 322,1031 319,1016 317,1001 315,986 312,969 310,952 307,937 305,923 L 308,921 C 329,952 349,979 370,1002 391,1025 413,1044 436,1059 460,1074 485,1085 513,1092 540,1099 570,1102 604,1102 649,1102 688,1094 722,1077 756,1060 785,1037 808,1006 832,975 850,938 862,895 874,851 880,802 880,748 880,715 879,678 876,639 873,599 869,558 863,516 848,421 829,340 804,273 780,205 751,149 717,106 683,63 644,31 600,11 556,-10 507,-20 453,-20 Z M 563,963 C 528,963 494,957 462,944 430,931 400,910 373,879 346,848 322,806 301,754 281,702 264,636 251,556 240,490 235,431 236,378 236,335 240,297 250,264 259,231 273,203 290,181 307,158 328,141 353,130 377,119 405,113 435,113 470,113 501,119 528,132 556,144 581,165 603,196 626,226 645,267 663,318 680,369 695,433 707,510 720,591 726,659 726,716 726,798 713,860 687,901 661,942 620,963 563,963 Z"/>
<glyph unicode="o" horiz-adv-x="848" d="M 881,683 C 881,648 879,614 876,579 873,544 867,506 859,467 842,379 820,304 791,242 762,180 727,130 688,91 649,52 605,24 557,7 509,-11 458,-20 403,-20 350,-20 303,-10 260,10 217,29 181,58 151,96 120,133 97,179 80,234 63,288 55,350 55,419 56,450 57,483 60,516 62,549 66,584 73,620 89,704 111,776 139,837 166,897 199,947 238,986 276,1025 319,1054 368,1073 416,1092 469,1101 526,1101 584,1101 635,1092 679,1073 723,1054 760,1027 790,991 820,955 843,911 858,860 873,808 881,749 881,683 Z M 728,683 C 728,734 723,778 714,814 705,850 691,880 674,903 656,926 635,942 610,953 585,964 556,969 525,969 496,969 467,965 438,957 409,948 381,931 354,906 327,881 303,845 281,798 259,751 241,689 226,612 219,575 214,541 211,508 208,475 207,444 207,416 207,361 212,315 223,276 233,237 247,206 266,182 284,158 305,141 330,130 355,119 382,113 412,113 441,113 470,117 499,125 528,133 555,150 581,176 606,201 630,238 652,285 673,332 691,395 706,473 713,513 719,550 722,583 725,616 727,650 728,683 Z"/>
<glyph unicode="n" horiz-adv-x="848" d="M 588,0 L 688,645 C 692,672 696,698 700,725 704,752 706,775 706,795 706,848 695,889 672,918 649,947 611,962 559,962 524,962 491,954 459,937 428,920 400,896 375,865 349,834 328,796 309,753 291,709 278,660 269,607 L 175,0 28,0 160,852 C 164,874 168,898 171,923 L 182,994 191,1051 C 194,1067 195,1077 196,1082 L 335,1082 C 335,1079 336,1069 338,1054 L 331,1004 322,945 C 319,926 317,910 314,897 L 315,897 C 334,928 353,957 373,982 392,1007 414,1029 438,1047 461,1064 487,1078 516,1087 545,1096 577,1101 614,1101 695,1101 756,1077 797,1028 838,979 858,909 858,817 858,804 856,783 851,752 846,721 842,693 838,668 L 733,0 588,0 Z"/>
<glyph unicode="i" horiz-adv-x="398" d="M 237,1312 L 264,1484 411,1484 384,1312 237,1312 Z M 27,0 L 200,1082 347,1082 174,0 27,0 Z"/>
<glyph unicode="h" horiz-adv-x="848" d="M 320,897 C 339,928 358,957 377,982 396,1007 418,1029 441,1047 465,1064 491,1078 519,1087 547,1096 580,1101 616,1101 696,1101 756,1077 796,1028 837,979 857,909 857,817 857,796 855,770 850,740 845,710 841,682 837,657 L 735,0 587,0 687,646 C 691,672 695,698 699,725 703,752 705,775 705,795 705,848 694,889 671,918 648,947 610,962 558,962 524,962 491,953 460,936 429,919 401,895 376,864 351,833 330,796 312,752 294,709 281,660 272,606 L 175,0 28,0 265,1484 412,1484 350,1099 C 347,1077 343,1055 340,1033 336,1011 333,990 330,972 327,953 325,937 323,924 320,911 319,902 318,897 L 320,897 Z"/>
<glyph unicode="f" horiz-adv-x="583" d="M 356,951 L 204,0 57,0 209,951 84,951 105,1082 230,1082 249,1204 C 256,1243 264,1280 274,1314 285,1348 300,1378 319,1403 338,1428 363,1448 394,1463 425,1477 463,1484 510,1484 527,1484 545,1483 564,1481 583,1479 599,1476 612,1472 L 591,1335 C 586,1336 581,1337 574,1338 568,1339 561,1340 554,1341 547,1342 540,1342 533,1343 527,1343 521,1343 516,1343 495,1343 478,1339 464,1332 450,1325 438,1314 429,1300 420,1286 413,1269 408,1249 403,1228 398,1205 393,1179 L 377,1082 551,1082 529,951 356,951 Z"/>
<glyph unicode="e" horiz-adv-x="821" d="M 210,503 C 207,484 206,466 205,447 204,428 203,409 203,390 203,301 221,233 258,186 294,139 348,115 421,115 452,115 480,120 505,130 530,139 553,152 574,169 594,185 612,204 628,226 643,247 656,270 667,294 L 780,231 C 767,201 750,171 730,142 710,112 686,85 657,61 628,37 593,18 552,3 511,-12 461,-20 404,-20 349,-20 301,-10 258,9 215,28 178,55 148,92 118,128 95,172 80,225 65,278 57,338 57,405 57,510 69,606 92,692 115,778 147,851 188,912 229,973 278,1020 334,1053 390,1086 451,1102 517,1102 577,1102 629,1092 674,1073 718,1054 755,1027 784,992 813,957 835,916 850,868 865,819 872,766 872,708 872,694 872,679 871,662 870,645 868,628 867,610 865,592 863,574 861,556 858,537 855,520 852,503 L 210,503 Z M 722,641 C 723,654 724,667 725,679 725,690 725,702 725,713 725,757 720,795 710,828 700,860 686,887 668,908 650,929 629,944 604,954 579,964 551,969 520,969 494,969 467,964 438,955 409,945 381,928 354,903 327,878 303,845 281,803 259,760 242,706 230,641 L 722,641 Z"/>
</font>
</defs>
<defs class="TextShapeIndex">
<g ooo:slide="id1" ooo:id-list="id3 id4 id5 id6 id7 id8 id9 id10"/>
</defs>
<defs class="EmbeddedBulletChars">
<g id="bullet-char-template-57356" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 580,1141 L 1163,571 580,0 -4,571 580,1141 Z"/>
</g>
<g id="bullet-char-template-57354" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 8,1128 L 1137,1128 1137,0 8,0 8,1128 Z"/>
</g>
<g id="bullet-char-template-10146" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 174,0 L 602,739 174,1481 1456,739 174,0 Z M 1358,739 L 309,1346 659,739 1358,739 Z"/>
</g>
<g id="bullet-char-template-10132" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 2015,739 L 1276,0 717,0 1260,543 174,543 174,936 1260,936 717,1481 1274,1481 2015,739 Z"/>
</g>
<g id="bullet-char-template-10007" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 0,-2 C -7,14 -16,27 -25,37 L 356,567 C 262,823 215,952 215,954 215,979 228,992 255,992 264,992 276,990 289,987 310,991 331,999 354,1012 L 381,999 492,748 772,1049 836,1024 860,1049 C 881,1039 901,1025 922,1006 886,937 835,863 770,784 769,783 710,716 594,584 L 774,223 C 774,196 753,168 711,139 L 727,119 C 717,90 699,76 672,76 641,76 570,178 457,381 L 164,-76 C 142,-110 111,-127 72,-127 30,-127 9,-110 8,-76 1,-67 -2,-52 -2,-32 -2,-23 -1,-13 0,-2 Z"/>
</g>
<g id="bullet-char-template-10004" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 285,-33 C 182,-33 111,30 74,156 52,228 41,333 41,471 41,549 55,616 82,672 116,743 169,778 240,778 293,778 328,747 346,684 L 369,508 C 377,444 397,411 428,410 L 1163,1116 C 1174,1127 1196,1133 1229,1133 1271,1133 1292,1118 1292,1087 L 1292,965 C 1292,929 1282,901 1262,881 L 442,47 C 390,-6 338,-33 285,-33 Z"/>
</g>
<g id="bullet-char-template-9679" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 813,0 C 632,0 489,54 383,161 276,268 223,411 223,592 223,773 276,916 383,1023 489,1130 632,1184 813,1184 992,1184 1136,1130 1245,1023 1353,916 1407,772 1407,592 1407,412 1353,268 1245,161 1136,54 992,0 813,0 Z"/>
</g>
<g id="bullet-char-template-8226" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 346,457 C 273,457 209,483 155,535 101,586 74,649 74,723 74,796 101,859 155,911 209,963 273,989 346,989 419,989 480,963 531,910 582,859 608,796 608,723 608,648 583,586 532,535 482,483 420,457 346,457 Z"/>
</g>
<g id="bullet-char-template-8211" transform="scale(0.00048828125,-0.00048828125)">
<path d="M -4,459 L 1135,459 1135,606 -4,606 -4,459 Z"/>
</g>
<g id="bullet-char-template-61548" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 173,740 C 173,903 231,1043 346,1159 462,1274 601,1332 765,1332 928,1332 1067,1274 1183,1159 1299,1043 1357,903 1357,740 1357,577 1299,437 1183,322 1067,206 928,148 765,148 601,148 462,206 346,322 231,437 173,577 173,740 Z"/>
</g>
</defs>
<g>
<g id="id2" class="Master_Slide">
<g id="bg-id2" class="Background"/>
<g id="bo-id2" class="BackgroundObjects"/>
</g>
</g>
<g class="SlideGroup">
<g>
<g id="container-id1">
<g id="id1" class="Slide" clip-path="url(#presentation_clip_path)">
<g class="Page">
<g class="com.sun.star.drawing.CustomShape">
<g id="id3">
<rect class="BoundingBox" stroke="none" fill="none" x="5152" y="1091" width="4724" height="731"/>
<path fill="rgb(204,255,0)" stroke="none" d="M 7514,1821 L 5152,1821 5152,1091 9875,1091 9875,1821 7514,1821 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id4">
<rect class="BoundingBox" stroke="none" fill="none" x="2168" y="1091" width="2439" height="731"/>
<path fill="rgb(255,255,153)" stroke="none" d="M 3387,1821 L 2168,1821 2168,1091 4606,1091 4606,1821 3387,1821 Z"/>
</g>
</g>
<g class="TextShape">
<g id="id5">
<rect class="BoundingBox" stroke="none" fill="none" x="500" y="1000" width="10713" height="879"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="564px" font-weight="400"><tspan class="TextPosition" x="750" y="1633"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">http://user:pass@www.example.com:80</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id6">
<rect class="BoundingBox" stroke="none" fill="none" x="2172" y="713" width="2418" height="434"/>
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 4580,1137 C 4580,1033 4481,929 4381,929 L 3581,929 C 3481,929 3381,825 3381,722 3381,825 3281,929 3181,929 L 2381,929 C 2281,929 2181,1033 2181,1137"/>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id7">
<rect class="BoundingBox" stroke="none" fill="none" x="5163" y="690" width="4723" height="441"/>
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 9876,1121 C 9876,1015 9680,910 9484,910 L 7917,910 C 7720,910 7524,804 7524,699 7524,804 7328,910 7132,910 L 5564,910 C 5368,910 5172,1015 5172,1121"/>
</g>
</g>
<g class="TextShape">
<g id="id8">
<rect class="BoundingBox" stroke="none" fill="none" x="2540" y="200" width="1743" height="650"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Liberation Sans Narrow, sans-serif" font-size="423px" font-style="italic" font-weight="400"><tspan class="TextPosition" x="2790" y="634"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">userinfo</tspan></tspan></tspan></text>
</g>
</g>
<g class="TextShape">
<g id="id9">
<rect class="BoundingBox" stroke="none" fill="none" x="6932" y="200" width="1163" height="650"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Liberation Sans Narrow, sans-serif" font-size="423px" font-style="italic" font-weight="400"><tspan class="TextPosition" x="7182" y="634"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">host</tspan></tspan></tspan></text>
</g>
</g>
<g class="TextShape">
<g id="id10">
<rect class="BoundingBox" stroke="none" fill="none" x="9773" y="200" width="1104" height="650"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Liberation Sans Narrow, sans-serif" font-size="423px" font-style="italic" font-weight="400"><tspan class="TextPosition" x="10023" y="634"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">port</tspan></tspan></tspan></text>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -0,0 +1,152 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.2" width="107.95mm" height="40.64mm" viewBox="0 0 10795 4064" preserveAspectRatio="xMidYMid" fill-rule="evenodd" stroke-width="28.222" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" xmlns:ooo="http://xml.openoffice.org/svg/export" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:presentation="http://sun.com/xmlns/staroffice/presentation" xmlns:smil="http://www.w3.org/2001/SMIL20/" xmlns:anim="urn:oasis:names:tc:opendocument:xmlns:animation:1.0" xml:space="preserve">
<defs class="ClipPathGroup">
<clipPath id="presentation_clip_path" clipPathUnits="userSpaceOnUse">
<rect x="0" y="0" width="10795" height="4064"/>
</clipPath>
<clipPath id="presentation_clip_path_shrink" clipPathUnits="userSpaceOnUse">
<rect x="10" y="4" width="10774" height="4056"/>
</clipPath>
</defs>
<defs>
<font id="EmbeddedFont_1" horiz-adv-x="2048">
<font-face font-family="Liberation Mono embedded" units-per-em="2048" font-weight="normal" font-style="normal" ascent="1693" descent="609"/>
<missing-glyph horiz-adv-x="2048" d="M 0,0 L 2047,0 2047,2047 0,2047 0,0 Z"/>
<glyph unicode="w" horiz-adv-x="1218" d="M 1018,0 L 814,0 671,471 614,673 575,534 407,0 204,0 21,1082 199,1082 292,475 C 314,299 325,190 325,149 351,256 370,327 383,363 L 518,787 711,787 841,362 C 862,292 881,221 896,149 896,169 897,194 900,224 903,254 906,285 910,317 914,348 918,378 922,407 926,436 929,458 931,475 L 1032,1082 1208,1082 1018,0 Z"/>
<glyph unicode="v" horiz-adv-x="1112" d="M 715,0 L 502,0 69,1082 271,1082 539,378 556,325 608,141 643,258 682,376 958,1082 1159,1082 715,0 Z"/>
<glyph unicode="u" horiz-adv-x="874" d="M 365,1082 L 365,396 C 365,292 381,220 414,180 447,139 505,119 589,119 675,119 743,148 793,207 843,266 868,348 868,455 L 868,1082 1049,1082 1049,231 C 1049,105 1051,28 1055,0 L 885,0 C 884,3 884,12 883,27 882,42 882,59 881,78 880,97 878,132 877,185 L 874,185 C 833,110 785,58 731,27 676,-4 609,-20 528,-20 409,-20 323,10 268,69 213,128 185,225 185,361 L 185,1082 365,1082 Z"/>
<glyph unicode="t" horiz-adv-x="821" d="M 190,940 L 190,1082 360,1082 418,1364 538,1364 538,1082 970,1082 970,940 538,940 538,288 C 538,235 552,196 581,171 609,146 655,133 720,133 809,133 908,144 1017,167 L 1017,30 C 904,-1 793,-16 682,-16 574,-16 493,7 439,53 385,98 358,170 358,269 L 358,940 190,940 Z"/>
<glyph unicode="s" horiz-adv-x="900" d="M 1060,309 C 1060,206 1021,126 944,68 866,9 758,-20 621,-20 484,-20 379,2 308,45 236,88 189,155 167,248 L 326,279 C 339,222 366,180 408,154 449,127 520,114 621,114 801,114 891,171 891,285 891,328 875,362 842,389 809,415 759,436 692,453 516,496 404,530 357,555 310,580 273,611 248,648 223,685 210,731 210,786 210,884 245,961 316,1016 387,1071 489,1099 623,1099 740,1099 834,1077 904,1033 974,988 1018,924 1035,839 L 873,819 C 866,867 842,903 802,928 762,953 702,965 623,965 460,965 378,915 378,814 378,774 392,742 420,718 447,694 492,675 553,660 L 672,629 C 781,602 859,576 907,550 954,524 992,492 1019,453 1046,414 1060,366 1060,309 Z"/>
<glyph unicode="r" horiz-adv-x="821" d="M 1045,918 C 970,931 900,937 833,937 726,937 639,897 573,816 507,735 474,633 474,508 L 474,0 294,0 294,701 C 294,752 290,811 281,880 272,949 259,1016 242,1082 L 413,1082 C 440,990 456,907 461,832 L 466,832 C 499,907 532,962 564,997 596,1032 634,1058 678,1076 722,1093 776,1102 839,1102 908,1102 977,1096 1045,1085 L 1045,918 Z"/>
<glyph unicode="l" horiz-adv-x="847" d="M 835,147 L 1116,142 1116,0 746,4 C 671,17 616,47 581,94 566,114 558,169 556,258 L 556,1342 267,1342 267,1484 736,1484 736,237 C 737,207 745,185 761,170 775,157 800,150 835,147 Z M 556,142 L 556,258 556,142 Z"/>
<glyph unicode="i" horiz-adv-x="1007" d="M 745,142 L 1125,142 1125,0 143,0 143,142 565,142 565,940 246,940 246,1082 745,1082 745,142 Z M 545,1292 L 545,1484 745,1484 745,1292 545,1292 Z"/>
<glyph unicode="e" horiz-adv-x="954" d="M 322,503 C 322,382 349,287 403,218 456,149 530,115 623,115 692,115 752,130 804,160 855,189 890,230 907,281 L 1065,236 C 1036,153 982,90 904,46 825,2 732,-20 623,-20 466,-20 345,29 260,127 175,225 133,365 133,548 133,726 175,863 258,959 341,1054 460,1102 617,1102 774,1102 892,1054 973,959 1054,864 1094,720 1094,527 L 1094,503 322,503 Z M 619,969 C 530,969 459,940 407,882 355,823 327,743 324,641 L 908,641 C 889,860 793,969 619,969 Z"/>
<glyph unicode="c" horiz-adv-x="980" d="M 130,542 C 130,722 173,860 259,957 345,1054 469,1102 632,1102 753,1102 853,1073 932,1015 1011,956 1059,878 1078,779 L 886,765 C 875,826 849,874 806,909 763,944 703,961 624,961 519,961 442,928 393,863 344,798 319,692 319,546 319,398 344,290 393,222 442,153 518,119 623,119 695,119 755,137 802,172 849,207 879,261 890,334 L 1080,322 C 1071,258 1047,200 1008,148 968,95 915,54 850,25 785,-5 712,-20 631,-20 468,-20 343,28 258,124 173,220 130,359 130,542 Z"/>
<glyph unicode="b" horiz-adv-x="927" d="M 1090,546 C 1090,357 1056,215 989,121 921,27 824,-20 698,-20 535,-20 424,41 364,164 L 362,164 C 362,132 361,99 359,64 356,29 354,8 353,0 L 179,0 C 183,36 185,110 185,223 L 185,1484 365,1484 365,1061 C 365,1018 364,965 361,904 L 365,904 C 426,1037 537,1104 699,1104 960,1104 1090,918 1090,546 Z M 904,540 C 904,689 884,798 843,865 802,932 737,965 650,965 551,965 478,929 433,856 388,783 365,672 365,524 365,385 387,281 431,214 475,147 547,113 648,113 738,113 803,148 844,218 884,287 904,395 904,540 Z"/>
<glyph unicode="a" horiz-adv-x="1059" d="M 1101,111 C 1118,111 1138,113 1160,118 L 1160,6 C 1115,-5 1068,-10 1021,-10 954,-10 906,8 876,43 845,78 828,132 824,207 L 818,207 C 775,126 724,68 665,33 606,-2 533,-20 446,-20 341,-20 261,9 208,66 155,123 128,202 128,302 128,535 279,653 582,656 L 818,660 818,719 C 818,806 800,869 765,908 730,946 673,965 596,965 517,965 461,951 426,923 391,895 371,852 364,793 L 176,810 C 207,1005 348,1102 599,1102 732,1102 833,1071 900,1009 967,946 1000,856 1000,738 L 1000,272 C 1000,219 1007,179 1021,152 1035,125 1062,111 1101,111 Z M 492,117 C 556,117 613,132 662,163 711,194 750,235 777,286 804,337 818,390 818,445 L 818,534 628,530 C 549,529 489,520 448,504 407,488 375,464 352,431 329,398 317,354 317,299 317,244 332,200 362,167 391,134 435,117 492,117 Z"/>
<glyph unicode="_" horiz-adv-x="1271" d="M -5,-220 L -5,-124 1233,-124 1233,-220 -5,-220 Z"/>
</font>
</defs>
<defs class="TextShapeIndex">
<g ooo:slide="id1" ooo:id-list="id3 id4 id5 id6 id7 id8 id9 id10 id11 id12"/>
</defs>
<defs class="EmbeddedBulletChars">
<g id="bullet-char-template-57356" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 580,1141 L 1163,571 580,0 -4,571 580,1141 Z"/>
</g>
<g id="bullet-char-template-57354" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 8,1128 L 1137,1128 1137,0 8,0 8,1128 Z"/>
</g>
<g id="bullet-char-template-10146" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 174,0 L 602,739 174,1481 1456,739 174,0 Z M 1358,739 L 309,1346 659,739 1358,739 Z"/>
</g>
<g id="bullet-char-template-10132" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 2015,739 L 1276,0 717,0 1260,543 174,543 174,936 1260,936 717,1481 1274,1481 2015,739 Z"/>
</g>
<g id="bullet-char-template-10007" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 0,-2 C -7,14 -16,27 -25,37 L 356,567 C 262,823 215,952 215,954 215,979 228,992 255,992 264,992 276,990 289,987 310,991 331,999 354,1012 L 381,999 492,748 772,1049 836,1024 860,1049 C 881,1039 901,1025 922,1006 886,937 835,863 770,784 769,783 710,716 594,584 L 774,223 C 774,196 753,168 711,139 L 727,119 C 717,90 699,76 672,76 641,76 570,178 457,381 L 164,-76 C 142,-110 111,-127 72,-127 30,-127 9,-110 8,-76 1,-67 -2,-52 -2,-32 -2,-23 -1,-13 0,-2 Z"/>
</g>
<g id="bullet-char-template-10004" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 285,-33 C 182,-33 111,30 74,156 52,228 41,333 41,471 41,549 55,616 82,672 116,743 169,778 240,778 293,778 328,747 346,684 L 369,508 C 377,444 397,411 428,410 L 1163,1116 C 1174,1127 1196,1133 1229,1133 1271,1133 1292,1118 1292,1087 L 1292,965 C 1292,929 1282,901 1262,881 L 442,47 C 390,-6 338,-33 285,-33 Z"/>
</g>
<g id="bullet-char-template-9679" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 813,0 C 632,0 489,54 383,161 276,268 223,411 223,592 223,773 276,916 383,1023 489,1130 632,1184 813,1184 992,1184 1136,1130 1245,1023 1353,916 1407,772 1407,592 1407,412 1353,268 1245,161 1136,54 992,0 813,0 Z"/>
</g>
<g id="bullet-char-template-8226" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 346,457 C 273,457 209,483 155,535 101,586 74,649 74,723 74,796 101,859 155,911 209,963 273,989 346,989 419,989 480,963 531,910 582,859 608,796 608,723 608,648 583,586 532,535 482,483 420,457 346,457 Z"/>
</g>
<g id="bullet-char-template-8211" transform="scale(0.00048828125,-0.00048828125)">
<path d="M -4,459 L 1135,459 1135,606 -4,606 -4,459 Z"/>
</g>
<g id="bullet-char-template-61548" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 173,740 C 173,903 231,1043 346,1159 462,1274 601,1332 765,1332 928,1332 1067,1274 1183,1159 1299,1043 1357,903 1357,740 1357,577 1299,437 1183,322 1067,206 928,148 765,148 601,148 462,206 346,322 231,437 173,577 173,740 Z"/>
</g>
</defs>
<g>
<g id="id2" class="Master_Slide">
<g id="bg-id2" class="Background"/>
<g id="bo-id2" class="BackgroundObjects"/>
</g>
</g>
<g class="SlideGroup">
<g>
<g id="container-id1">
<g id="id1" class="Slide" clip-path="url(#presentation_clip_path)">
<g class="Page">
<g class="com.sun.star.drawing.CustomShape">
<g id="id3">
<rect class="BoundingBox" stroke="none" fill="none" x="-1" y="0" width="10796" height="4065"/>
<path fill="rgb(255,255,255)" stroke="none" d="M 5397,4064 L -1,4064 -1,0 10794,0 10794,4064 5397,4064 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id4">
<rect class="BoundingBox" stroke="none" fill="none" x="2913" y="290" width="3512" height="654"/>
<path fill="rgb(221,221,221)" stroke="none" d="M 4669,934 L 2922,934 2922,299 6415,299 6415,934 4669,934 Z"/>
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 4669,934 L 2922,934 2922,299 6415,299 6415,934 4669,934 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Liberation Mono, monospace" font-size="353px" font-weight="400"><tspan class="TextPosition" x="3293" y="710"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">url_view_base</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id5">
<rect class="BoundingBox" stroke="none" fill="none" x="4820" y="1560" width="3511" height="654"/>
<path fill="rgb(221,221,221)" stroke="none" d="M 6575,2204 L 4829,2204 4829,1569 8321,1569 8321,2204 6575,2204 Z"/>
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 6575,2204 L 4829,2204 4829,1569 8321,1569 8321,2204 6575,2204 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Liberation Mono, monospace" font-size="353px" font-weight="400"><tspan class="TextPosition" x="5729" y="1980"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">url_base</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id6">
<rect class="BoundingBox" stroke="none" fill="none" x="2596" y="2830" width="3511" height="654"/>
<path fill="rgb(255,255,255)" stroke="none" d="M 4351,3474 L 2605,3474 2605,2839 6097,2839 6097,3474 4351,3474 Z"/>
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 4351,3474 L 2605,3474 2605,2839 6097,2839 6097,3474 4351,3474 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Liberation Mono, monospace" font-size="353px" font-weight="400"><tspan class="TextPosition" x="4034" y="3250"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">url</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id7">
<rect class="BoundingBox" stroke="none" fill="none" x="6724" y="2830" width="3511" height="654"/>
<path fill="rgb(255,255,255)" stroke="none" d="M 8479,3474 L 6733,3474 6733,2839 10225,2839 10225,3474 8479,3474 Z"/>
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 8479,3474 L 6733,3474 6733,2839 10225,2839 10225,3474 8479,3474 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Liberation Mono, monospace" font-size="353px" font-weight="400"><tspan class="TextPosition" x="7421" y="3250"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">static_url</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id8">
<rect class="BoundingBox" stroke="none" fill="none" x="691" y="1560" width="3511" height="654"/>
<path fill="rgb(255,255,255)" stroke="none" d="M 2446,2204 L 700,2204 700,1569 4192,1569 4192,2204 2446,2204 Z"/>
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 2446,2204 L 700,2204 700,1569 4192,1569 4192,2204 2446,2204 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Liberation Mono, monospace" font-size="353px" font-weight="400"><tspan class="TextPosition" x="1600" y="1980"><tspan fill="rgb(0,0,0)" stroke="none" style="white-space: pre">url_view</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.ConnectorShape">
<g id="id9">
<rect class="BoundingBox" stroke="none" fill="none" x="2437" y="926" width="2242" height="654"/>
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 4669,935 L 2446,1570"/>
</g>
</g>
<g class="com.sun.star.drawing.ConnectorShape">
<g id="id10">
<rect class="BoundingBox" stroke="none" fill="none" x="4660" y="926" width="1925" height="654"/>
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 4669,935 L 6575,1570"/>
</g>
</g>
<g class="com.sun.star.drawing.ConnectorShape">
<g id="id11">
<rect class="BoundingBox" stroke="none" fill="none" x="4342" y="2196" width="2243" height="654"/>
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 6575,2205 L 4351,2840"/>
</g>
</g>
<g class="com.sun.star.drawing.ConnectorShape">
<g id="id12">
<rect class="BoundingBox" stroke="none" fill="none" x="6566" y="2196" width="1923" height="654"/>
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 6575,2205 L 8479,2840"/>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 154 KiB

View File

@@ -0,0 +1,145 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.2" width="165.1mm" height="16.51mm" viewBox="0 0 16510 1651" preserveAspectRatio="xMidYMid" fill-rule="evenodd" stroke-width="28.222" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" xmlns:ooo="http://xml.openoffice.org/svg/export" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:presentation="http://sun.com/xmlns/staroffice/presentation" xmlns:smil="http://www.w3.org/2001/SMIL20/" xmlns:anim="urn:oasis:names:tc:opendocument:xmlns:animation:1.0" xml:space="preserve">
<defs class="ClipPathGroup">
<clipPath id="presentation_clip_path" clipPathUnits="userSpaceOnUse">
<rect x="0" y="0" width="16510" height="1651"/>
</clipPath>
<clipPath id="presentation_clip_path_shrink" clipPathUnits="userSpaceOnUse">
<rect x="16" y="1" width="16477" height="1648"/>
</clipPath>
</defs>
<defs>
<font id="EmbeddedFont_1" horiz-adv-x="2048">
<font-face font-family="Liberation Sans embedded" units-per-em="2048" font-weight="normal" font-style="italic" ascent="1852" descent="423"/>
<missing-glyph horiz-adv-x="2048" d="M 0,0 L 2047,0 2047,2047 0,2047 0,0 Z"/>
<glyph unicode="y" horiz-adv-x="1271" d="M 16,-425 C -32,-425 -76,-420 -116,-411 L -85,-277 C -55,-282 -29,-285 -8,-285 55,-285 111,-264 160,-221 208,-178 255,-116 302,-35 L 329,12 112,1082 295,1082 407,484 C 417,431 426,374 435,314 444,253 448,214 449,196 456,211 464,229 475,250 486,271 637,549 928,1082 L 1127,1082 501,0 C 427,-129 368,-219 323,-270 278,-322 231,-361 182,-386 133,-412 77,-425 16,-425 Z"/>
<glyph unicode="u" horiz-adv-x="1033" d="M 415,1082 L 289,437 C 277,378 271,328 271,287 271,176 331,120 450,120 534,120 608,152 672,216 736,280 779,367 800,476 L 918,1082 1098,1082 932,231 C 919,168 906,91 893,0 L 723,0 C 723,7 727,33 734,78 741,122 746,158 751,185 L 748,185 C 693,108 637,55 582,26 526,-4 460,-19 383,-19 284,-19 210,5 161,54 111,103 86,173 86,265 86,308 93,362 107,429 L 234,1082 415,1082 Z"/>
<glyph unicode="t" horiz-adv-x="557" d="M 275,-20 C 218,-20 174,-3 142,31 109,65 93,110 93,166 93,203 98,246 108,296 L 234,951 109,951 135,1082 262,1082 367,1324 487,1324 440,1082 640,1082 614,951 414,951 289,306 C 281,266 277,234 277,211 277,152 307,123 367,123 395,123 428,128 467,137 L 448,4 C 382,-12 324,-20 275,-20 Z"/>
<glyph unicode="s" horiz-adv-x="1006" d="M 907,317 C 907,209 866,126 783,68 700,9 581,-20 425,-20 309,-20 217,0 149,39 80,78 32,139 5,223 L 152,279 C 174,220 208,178 255,151 301,124 363,111 441,111 536,111 609,127 658,160 707,192 732,239 732,301 732,342 715,377 681,405 647,432 575,463 465,497 380,525 316,552 273,579 230,606 198,637 175,673 152,708 141,750 141,797 141,894 180,968 257,1021 334,1073 443,1099 584,1099 820,1099 953,1014 982,844 L 819,819 C 804,872 777,910 736,933 695,956 641,968 572,968 489,968 426,955 382,929 337,902 315,865 315,817 315,789 322,765 336,746 350,727 370,710 397,695 423,680 484,657 579,627 664,600 728,573 771,546 814,519 847,486 871,449 895,412 907,368 907,317 Z"/>
<glyph unicode="r" horiz-adv-x="742" d="M 718,938 C 689,947 659,951 628,951 558,951 495,914 439,841 382,768 344,675 324,564 L 214,0 34,0 196,830 221,968 239,1082 409,1082 374,861 378,861 C 422,950 465,1012 508,1048 551,1084 600,1102 656,1102 687,1102 718,1097 751,1088 L 718,938 Z"/>
<glyph unicode="q" horiz-adv-x="1086" d="M 401,-21 C 296,-21 215,11 157,74 98,137 69,223 69,333 69,468 90,600 131,727 172,854 229,948 304,1009 378,1070 473,1101 588,1101 670,1101 737,1084 789,1049 840,1014 877,966 898,903 L 903,903 C 910,939 919,978 930,1021 941,1064 948,1089 952,1096 L 1125,1096 C 1110,1044 1088,946 1060,801 L 822,-425 642,-425 727,14 759,160 755,160 C 706,97 655,51 601,22 547,-7 480,-21 401,-21 Z M 453,118 C 519,118 576,132 623,159 670,186 709,226 742,281 775,335 800,403 819,485 838,566 847,639 847,704 847,787 826,852 784,899 741,945 682,968 607,968 526,968 461,944 414,896 367,847 329,768 300,659 271,549 257,451 257,365 257,283 273,221 304,180 335,139 385,118 453,118 Z"/>
<glyph unicode="p" horiz-adv-x="1139" d="M 554,-20 C 472,-20 405,-3 354,32 302,67 265,115 244,178 L 239,178 C 239,171 236,149 231,113 225,77 191,-102 128,-425 L -51,-425 198,861 C 213,935 225,1009 233,1082 L 400,1082 C 400,1064 398,1037 394,1000 389,963 386,936 383,921 L 387,921 C 436,984 487,1030 541,1059 595,1088 662,1102 741,1102 846,1102 927,1071 986,1008 1044,945 1073,858 1073,748 1073,613 1053,482 1012,355 971,228 913,133 839,72 764,11 669,-20 554,-20 Z M 689,963 C 623,963 567,950 520,923 473,896 433,855 400,801 367,746 342,678 323,597 304,515 295,442 295,377 295,294 316,229 359,183 401,136 460,113 535,113 618,113 683,138 731,189 779,239 817,319 844,429 871,538 885,634 885,716 885,798 869,860 838,901 807,942 757,963 689,963 Z"/>
<glyph unicode="o" horiz-adv-x="1033" d="M 1074,683 C 1074,596 1061,506 1034,413 1007,319 969,240 920,175 870,110 809,61 737,29 665,-4 583,-20 491,-20 360,-20 257,19 181,98 105,177 67,284 67,419 70,555 94,676 141,781 188,886 252,966 335,1020 418,1074 520,1101 642,1101 782,1101 889,1065 963,992 1037,919 1074,816 1074,683 Z M 888,683 C 888,874 805,969 640,969 550,969 478,946 424,900 369,853 327,783 297,689 267,595 252,504 252,416 252,317 273,242 316,191 359,139 421,113 502,113 571,113 626,125 668,148 709,171 746,207 777,256 808,305 834,367 854,443 873,519 885,599 888,683 Z"/>
<glyph unicode="n" horiz-adv-x="1033" d="M 717,0 L 843,645 C 855,704 861,754 861,795 861,906 801,962 682,962 598,962 524,930 460,866 396,802 353,715 332,606 L 214,0 34,0 200,851 C 213,914 226,991 239,1082 L 409,1082 C 409,1075 406,1049 399,1005 392,960 386,924 381,897 L 384,897 C 439,974 495,1027 551,1057 606,1086 672,1101 749,1101 848,1101 922,1077 972,1028 1021,979 1046,909 1046,817 1046,774 1039,720 1025,653 L 898,0 717,0 Z"/>
<glyph unicode="m" horiz-adv-x="1589" d="M 660,0 L 784,634 C 801,717 809,775 809,808 809,858 796,896 771,923 746,949 704,962 647,962 570,962 502,929 445,863 387,797 349,711 331,604 L 213,0 34,0 200,851 C 213,914 226,991 239,1082 L 409,1082 C 409,1075 406,1049 399,1005 392,960 386,924 381,897 L 384,897 C 433,973 482,1026 531,1056 580,1086 638,1101 706,1101 787,1101 851,1081 898,1042 945,1002 974,944 983,869 1038,956 1093,1016 1147,1050 1200,1084 1262,1101 1331,1101 1421,1101 1490,1077 1539,1028 1587,979 1611,909 1611,817 1611,774 1604,720 1590,653 L 1463,0 1285,0 1409,634 C 1426,717 1434,775 1434,808 1434,858 1421,896 1396,923 1371,949 1329,962 1272,962 1195,962 1127,930 1070,865 1013,800 975,714 956,607 L 838,0 660,0 Z"/>
<glyph unicode="i" horiz-adv-x="478" d="M 287,1312 L 321,1484 501,1484 467,1312 287,1312 Z M 33,0 L 243,1082 423,1082 212,0 33,0 Z"/>
<glyph unicode="h" horiz-adv-x="1033" d="M 383,897 C 438,974 494,1027 550,1057 605,1086 671,1101 748,1101 847,1101 921,1077 971,1028 1020,979 1045,909 1045,817 1045,774 1038,720 1024,653 L 897,0 716,0 842,645 C 854,704 860,754 860,795 860,906 800,962 681,962 597,962 523,930 459,866 395,802 352,715 331,606 L 213,0 34,0 322,1484 502,1484 427,1098 C 415,1031 399,964 380,897 L 383,897 Z"/>
<glyph unicode="g" horiz-adv-x="1139" d="M 397,-425 C 171,-425 40,-341 4,-173 L 167,-131 C 190,-236 268,-288 401,-288 496,-288 569,-263 621,-214 673,-165 710,-85 731,27 L 765,201 763,201 C 722,142 685,99 654,73 622,47 586,27 547,13 507,-1 461,-8 410,-8 308,-8 226,26 164,93 101,160 70,248 70,359 70,448 83,543 108,646 133,749 167,834 210,902 253,970 306,1020 368,1053 430,1085 503,1101 588,1101 669,1101 737,1082 793,1044 848,1006 884,957 900,897 L 902,897 C 907,922 915,957 927,1004 939,1050 947,1076 950,1082 L 1121,1082 1102,1000 1072,858 911,31 C 879,-130 822,-247 739,-318 656,-389 542,-425 397,-425 Z M 259,373 C 259,292 277,231 312,189 347,146 398,125 466,125 538,125 603,151 662,204 721,256 766,328 799,419 831,510 847,605 847,704 847,787 826,852 783,899 740,945 682,968 607,968 547,968 497,956 457,931 417,906 383,868 356,815 329,762 306,692 287,605 268,517 259,440 259,373 Z"/>
<glyph unicode="f" horiz-adv-x="689" d="M 434,951 L 249,0 69,0 254,951 102,951 128,1082 280,1082 303,1204 C 318,1279 339,1335 364,1372 389,1409 422,1437 463,1456 504,1475 557,1484 622,1484 671,1484 713,1480 746,1472 L 720,1335 675,1341 629,1343 C 587,1343 555,1332 533,1311 510,1289 492,1245 479,1179 L 460,1082 671,1082 645,951 434,951 Z"/>
<glyph unicode="e" horiz-adv-x="1033" d="M 256,503 C 252,480 249,442 247,390 247,301 269,233 314,186 358,139 425,115 514,115 579,115 637,131 690,163 743,195 784,239 813,294 L 951,231 C 902,144 841,80 766,40 691,0 600,-20 493,-20 359,-20 255,17 181,92 106,166 69,270 69,405 69,539 93,659 140,766 187,872 254,955 340,1014 426,1073 523,1102 630,1102 767,1102 873,1068 949,999 1025,930 1063,833 1063,708 1063,638 1055,570 1039,503 L 256,503 Z M 880,641 L 884,713 C 884,796 863,859 820,903 777,947 715,969 634,969 545,969 470,941 409,884 348,827 305,746 280,641 L 880,641 Z"/>
<glyph unicode="c" horiz-adv-x="980" d="M 469,122 C 606,122 702,199 758,352 L 914,303 C 833,88 684,-20 465,-20 337,-20 239,16 170,89 101,162 67,264 67,395 67,528 91,652 140,767 188,882 252,966 332,1021 411,1075 509,1102 625,1102 738,1102 827,1074 893,1017 958,960 994,883 1001,784 L 824,759 C 820,824 800,874 764,909 728,944 680,961 619,961 535,961 467,939 415,894 363,849 323,777 294,679 265,581 251,484 251,389 251,211 324,122 469,122 Z"/>
<glyph unicode="a" horiz-adv-x="1060" d="M 927,-10 C 865,-10 820,3 792,29 763,54 749,92 749,143 L 754,207 748,207 C 693,123 635,64 576,31 517,-3 445,-20 361,-20 268,-20 192,8 134,64 75,120 46,191 46,278 46,401 90,495 179,558 267,621 408,654 601,657 L 833,660 C 846,725 852,768 852,787 852,848 834,893 799,922 764,951 715,965 652,965 573,965 513,951 472,923 431,894 402,851 384,793 L 206,822 C 236,920 288,991 363,1036 437,1080 537,1102 662,1102 776,1102 866,1075 933,1022 999,969 1032,897 1032,807 1032,764 1026,712 1013,650 L 939,272 C 932,240 928,211 928,184 928,135 955,111 1009,111 1027,111 1047,113 1069,118 L 1055,6 C 1011,-5 968,-10 927,-10 Z M 809,536 L 610,532 C 531,530 468,523 423,511 378,498 343,483 318,464 293,445 273,421 259,392 244,363 237,327 237,286 237,236 253,195 286,164 318,133 360,117 411,117 476,117 534,131 586,159 638,186 681,222 715,267 749,312 771,360 782,411 L 809,536 Z"/>
</font>
</defs>
<defs>
<font id="EmbeddedFont_2" horiz-adv-x="2048">
<font-face font-family="Liberation Sans embedded" units-per-em="2048" font-weight="bold" font-style="normal" ascent="1852" descent="423"/>
<missing-glyph horiz-adv-x="2048" d="M 0,0 L 2047,0 2047,2047 0,2047 0,0 Z"/>
<glyph unicode="”" horiz-adv-x="742" d="M 872,1217 C 872,1136 864,1065 847,1004 830,942 802,884 763,831 L 579,831 C 668,943 712,1049 712,1149 L 585,1149 585,1409 872,1409 872,1217 Z M 442,1217 C 442,1134 434,1062 417,1003 400,943 372,886 333,831 L 151,831 C 238,943 282,1049 282,1149 L 155,1149 155,1409 442,1409 442,1217 Z"/>
<glyph unicode="“" horiz-adv-x="742" d="M 579,831 L 579,1026 C 579,1109 588,1180 606,1241 624,1302 652,1358 690,1409 L 872,1409 C 831,1357 799,1303 776,1248 753,1192 741,1140 741,1092 L 868,1092 868,831 579,831 Z M 151,831 L 151,1026 C 151,1107 160,1178 177,1240 194,1301 222,1358 259,1409 L 442,1409 C 401,1357 369,1303 346,1248 323,1192 311,1140 311,1092 L 438,1092 438,831 151,831 Z"/>
<glyph unicode="?" horiz-adv-x="1060" d="M 1133,1026 C 1133,961 1119,903 1090,852 1061,801 1006,747 927,690 L 851,635 C 806,602 772,569 750,536 727,503 715,466 713,426 L 446,426 C 450,494 469,555 504,608 538,661 588,711 655,758 726,807 777,851 806,889 835,926 850,968 850,1014 850,1073 831,1119 793,1153 754,1187 700,1204 629,1204 562,1204 505,1184 460,1145 414,1106 387,1054 379,989 L 94,1001 C 112,1136 168,1242 261,1317 354,1392 476,1430 625,1430 783,1430 907,1394 998,1323 1088,1251 1133,1152 1133,1026 Z M 438,0 L 438,270 727,270 727,0 438,0 Z"/>
<glyph unicode=":" horiz-adv-x="319" d="M 197,752 L 197,1034 485,1034 485,752 197,752 Z M 197,0 L 197,281 485,281 485,0 197,0 Z"/>
<glyph unicode="/" horiz-adv-x="557" d="M 20,-41 L 311,1484 549,1484 263,-41 20,-41 Z"/>
<glyph unicode="#" horiz-adv-x="1086" d="M 909,862 L 840,530 1055,530 1055,381 809,381 727,0 571,0 651,381 344,381 264,0 111,0 190,381 35,381 35,530 223,530 293,862 86,862 86,1010 324,1010 408,1395 561,1395 479,1010 786,1010 870,1395 1026,1395 942,1010 1106,1010 1106,862 909,862 Z M 449,862 L 377,530 686,530 756,862 449,862 Z"/>
<glyph unicode=" " horiz-adv-x="556"/>
</font>
</defs>
<defs class="TextShapeIndex">
<g ooo:slide="id1" ooo:id-list="id3 id4 id5 id6 id7 id8"/>
</defs>
<defs class="EmbeddedBulletChars">
<g id="bullet-char-template-57356" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 580,1141 L 1163,571 580,0 -4,571 580,1141 Z"/>
</g>
<g id="bullet-char-template-57354" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 8,1128 L 1137,1128 1137,0 8,0 8,1128 Z"/>
</g>
<g id="bullet-char-template-10146" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 174,0 L 602,739 174,1481 1456,739 174,0 Z M 1358,739 L 309,1346 659,739 1358,739 Z"/>
</g>
<g id="bullet-char-template-10132" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 2015,739 L 1276,0 717,0 1260,543 174,543 174,936 1260,936 717,1481 1274,1481 2015,739 Z"/>
</g>
<g id="bullet-char-template-10007" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 0,-2 C -7,14 -16,27 -25,37 L 356,567 C 262,823 215,952 215,954 215,979 228,992 255,992 264,992 276,990 289,987 310,991 331,999 354,1012 L 381,999 492,748 772,1049 836,1024 860,1049 C 881,1039 901,1025 922,1006 886,937 835,863 770,784 769,783 710,716 594,584 L 774,223 C 774,196 753,168 711,139 L 727,119 C 717,90 699,76 672,76 641,76 570,178 457,381 L 164,-76 C 142,-110 111,-127 72,-127 30,-127 9,-110 8,-76 1,-67 -2,-52 -2,-32 -2,-23 -1,-13 0,-2 Z"/>
</g>
<g id="bullet-char-template-10004" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 285,-33 C 182,-33 111,30 74,156 52,228 41,333 41,471 41,549 55,616 82,672 116,743 169,778 240,778 293,778 328,747 346,684 L 369,508 C 377,444 397,411 428,410 L 1163,1116 C 1174,1127 1196,1133 1229,1133 1271,1133 1292,1118 1292,1087 L 1292,965 C 1292,929 1282,901 1262,881 L 442,47 C 390,-6 338,-33 285,-33 Z"/>
</g>
<g id="bullet-char-template-9679" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 813,0 C 632,0 489,54 383,161 276,268 223,411 223,592 223,773 276,916 383,1023 489,1130 632,1184 813,1184 992,1184 1136,1130 1245,1023 1353,916 1407,772 1407,592 1407,412 1353,268 1245,161 1136,54 992,0 813,0 Z"/>
</g>
<g id="bullet-char-template-8226" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 346,457 C 273,457 209,483 155,535 101,586 74,649 74,723 74,796 101,859 155,911 209,963 273,989 346,989 419,989 480,963 531,910 582,859 608,796 608,723 608,648 583,586 532,535 482,483 420,457 346,457 Z"/>
</g>
<g id="bullet-char-template-8211" transform="scale(0.00048828125,-0.00048828125)">
<path d="M -4,459 L 1135,459 1135,606 -4,606 -4,459 Z"/>
</g>
<g id="bullet-char-template-61548" transform="scale(0.00048828125,-0.00048828125)">
<path d="M 173,740 C 173,903 231,1043 346,1159 462,1274 601,1332 765,1332 928,1332 1067,1274 1183,1159 1299,1043 1357,903 1357,740 1357,577 1299,437 1183,322 1067,206 928,148 765,148 601,148 462,206 346,322 231,437 173,577 173,740 Z"/>
</g>
</defs>
<g>
<g id="id2" class="Master_Slide">
<g id="bg-id2" class="Background"/>
<g id="bo-id2" class="BackgroundObjects"/>
</g>
</g>
<g class="SlideGroup">
<g>
<g id="container-id1">
<g id="id1" class="Slide" clip-path="url(#presentation_clip_path)">
<g class="Page">
<g class="com.sun.star.drawing.CustomShape">
<g id="id3">
<rect class="BoundingBox" stroke="none" fill="none" x="0" y="-1" width="16511" height="1652"/>
<path fill="rgb(255,255,255)" stroke="none" d="M 8255,1650 L 0,1650 0,-1 16510,-1 16510,1650 8255,1650 Z"/>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id4">
<rect class="BoundingBox" stroke="none" fill="none" x="12990" y="290" width="3195" height="997"/>
<path fill="rgb(255,237,182)" stroke="none" d="M 13162,299 L 13162,299 C 13133,299 13105,307 13080,321 13056,335 13035,356 13021,381 13007,405 12999,433 12999,462 L 12999,1114 12999,1114 C 12999,1143 13007,1171 13021,1196 13035,1220 13056,1241 13080,1255 13105,1269 13133,1277 13162,1277 L 16012,1277 16012,1277 C 16041,1277 16069,1269 16094,1255 16118,1241 16139,1220 16153,1196 16167,1171 16175,1143 16175,1114 L 16175,462 16175,462 16175,462 C 16175,433 16167,405 16153,381 16139,356 16118,335 16094,321 16069,307 16041,299 16012,299 L 13162,299 Z"/>
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 13162,299 L 13162,299 C 13133,299 13105,307 13080,321 13056,335 13035,356 13021,381 13007,405 12999,433 12999,462 L 12999,1114 12999,1114 C 12999,1143 13007,1171 13021,1196 13035,1220 13056,1241 13080,1255 13105,1269 13133,1277 13162,1277 L 16012,1277 16012,1277 C 16041,1277 16069,1269 16094,1255 16118,1241 16139,1220 16153,1196 16167,1171 16175,1143 16175,1114 L 16175,462 16175,462 16175,462 C 16175,433 16167,405 16153,381 16139,356 16118,335 16094,321 16069,307 16041,299 16012,299 L 13162,299 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="388px" font-weight="700"><tspan class="TextPosition" x="13458" y="955"><tspan fill="rgb(51,51,51)" stroke="none" style="white-space: pre"></tspan><tspan fill="rgb(51,51,51)" stroke="none" style="white-space: pre">#” </tspan><tspan font-style="italic" font-weight="400" fill="rgb(51,51,51)" stroke="none" style="white-space: pre">fragment</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id5">
<rect class="BoundingBox" stroke="none" fill="none" x="9814" y="290" width="3196" height="997"/>
<path fill="rgb(255,220,255)" stroke="none" d="M 9987,299 L 9987,299 C 9958,299 9930,307 9906,321 9881,335 9860,356 9846,381 9832,405 9824,433 9824,462 L 9824,1114 9824,1114 C 9824,1143 9832,1171 9846,1196 9860,1220 9881,1241 9906,1255 9930,1269 9958,1277 9987,1277 L 12837,1277 12837,1277 C 12866,1277 12894,1269 12919,1255 12943,1241 12964,1220 12978,1196 12992,1171 13000,1143 13000,1114 L 13000,462 13000,462 13000,462 C 13000,433 12992,405 12978,381 12964,356 12943,335 12919,321 12894,307 12866,299 12837,299 L 9987,299 Z"/>
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 9987,299 L 9987,299 C 9958,299 9930,307 9906,321 9881,335 9860,356 9846,381 9832,405 9824,433 9824,462 L 9824,1114 9824,1114 C 9824,1143 9832,1171 9846,1196 9860,1220 9881,1241 9906,1255 9930,1269 9958,1277 9987,1277 L 12837,1277 12837,1277 C 12866,1277 12894,1269 12919,1255 12943,1241 12964,1220 12978,1196 12992,1171 13000,1143 13000,1114 L 13000,462 13000,462 13000,462 C 13000,433 12992,405 12978,381 12964,356 12943,335 12919,321 12894,307 12866,299 12837,299 L 9987,299 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="388px" font-weight="700"><tspan class="TextPosition" x="10556" y="955"><tspan fill="rgb(51,51,51)" stroke="none" style="white-space: pre"></tspan><tspan fill="rgb(51,51,51)" stroke="none" style="white-space: pre">?” </tspan><tspan font-style="italic" font-weight="400" fill="rgb(51,51,51)" stroke="none" style="white-space: pre">query</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id6">
<rect class="BoundingBox" stroke="none" fill="none" x="6640" y="290" width="3195" height="997"/>
<path fill="rgb(193,255,255)" stroke="none" d="M 6812,299 L 6812,299 C 6783,299 6755,307 6731,321 6706,335 6685,356 6671,381 6657,405 6649,433 6649,462 L 6649,1114 6649,1114 C 6649,1143 6657,1171 6671,1196 6685,1220 6706,1241 6731,1255 6755,1269 6783,1277 6812,1277 L 9662,1277 9662,1277 C 9691,1277 9719,1269 9744,1255 9768,1241 9789,1220 9803,1196 9817,1171 9825,1143 9825,1114 L 9825,462 9825,462 9825,462 C 9825,433 9817,405 9803,381 9789,356 9768,335 9744,321 9719,307 9691,299 9662,299 L 6812,299 Z"/>
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 6812,299 L 6812,299 C 6783,299 6755,307 6731,321 6706,335 6685,356 6671,381 6657,405 6649,433 6649,462 L 6649,1114 6649,1114 C 6649,1143 6657,1171 6671,1196 6685,1220 6706,1241 6731,1255 6755,1269 6783,1277 6812,1277 L 9662,1277 9662,1277 C 9691,1277 9719,1269 9744,1255 9768,1241 9789,1220 9803,1196 9817,1171 9825,1143 9825,1114 L 9825,462 9825,462 9825,462 C 9825,433 9817,405 9803,381 9789,356 9768,335 9744,321 9719,307 9691,299 9662,299 L 6812,299 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="388px" font-style="italic" font-weight="400"><tspan class="TextPosition" x="7858" y="955"><tspan fill="rgb(51,51,51)" stroke="none" style="white-space: pre">path</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id7">
<rect class="BoundingBox" stroke="none" fill="none" x="289" y="297" width="3196" height="998"/>
<path fill="rgb(255,255,177)" stroke="none" d="M 462,306 L 462,306 C 433,306 405,314 380,328 356,342 335,363 321,388 307,412 299,440 299,469 L 299,1121 299,1121 C 299,1150 307,1178 321,1203 335,1227 356,1248 381,1262 405,1276 433,1284 462,1284 L 3312,1284 3312,1284 C 3341,1284 3369,1276 3394,1262 3418,1248 3439,1227 3453,1203 3467,1178 3475,1150 3475,1121 L 3475,469 3475,469 3475,469 C 3475,440 3467,412 3453,387 3439,363 3418,342 3394,328 3369,314 3341,306 3312,306 L 462,306 Z"/>
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 462,306 L 462,306 C 433,306 405,314 380,328 356,342 335,363 321,388 307,412 299,440 299,469 L 299,1121 299,1121 C 299,1150 307,1178 321,1203 335,1227 356,1248 381,1262 405,1276 433,1284 462,1284 L 3312,1284 3312,1284 C 3341,1284 3369,1276 3394,1262 3418,1248 3439,1227 3453,1203 3467,1178 3475,1150 3475,1121 L 3475,469 3475,469 3475,469 C 3475,440 3467,412 3453,387 3439,363 3418,342 3394,328 3369,314 3341,306 3312,306 L 462,306 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="388px" font-style="italic" font-weight="400"><tspan class="TextPosition" x="890" y="962"><tspan fill="rgb(51,51,51)" stroke="none" style="white-space: pre">scheme</tspan><tspan font-style="normal" font-weight="700" fill="rgb(51,51,51)" stroke="none" style="white-space: pre"> “:”</tspan></tspan></tspan></text>
</g>
</g>
<g class="com.sun.star.drawing.CustomShape">
<g id="id8">
<rect class="BoundingBox" stroke="none" fill="none" x="3465" y="290" width="3196" height="997"/>
<path fill="rgb(215,255,195)" stroke="none" d="M 3637,299 L 3637,299 C 3608,299 3580,307 3555,321 3531,335 3510,356 3496,381 3482,405 3474,433 3474,462 L 3474,1114 3474,1114 C 3474,1143 3482,1171 3496,1196 3510,1220 3531,1241 3555,1255 3580,1269 3608,1277 3637,1277 L 6487,1277 6487,1277 C 6516,1277 6544,1269 6569,1255 6593,1241 6614,1220 6628,1196 6642,1171 6650,1143 6650,1114 L 6650,462 6650,462 6650,462 C 6650,433 6642,405 6628,381 6614,356 6593,335 6569,321 6544,307 6516,299 6487,299 L 3637,299 Z"/>
<path fill="none" stroke="rgb(0,0,0)" stroke-width="18" stroke-linejoin="round" d="M 3637,299 L 3637,299 C 3608,299 3580,307 3555,321 3531,335 3510,356 3496,381 3482,405 3474,433 3474,462 L 3474,1114 3474,1114 C 3474,1143 3482,1171 3496,1196 3510,1220 3531,1241 3555,1255 3580,1269 3608,1277 3637,1277 L 6487,1277 6487,1277 C 6516,1277 6544,1269 6569,1255 6593,1241 6614,1220 6628,1196 6642,1171 6650,1143 6650,1114 L 6650,462 6650,462 6650,462 C 6650,433 6642,405 6628,381 6614,356 6593,335 6569,321 6544,307 6516,299 6487,299 L 3637,299 Z"/>
<text class="SVGTextShape"><tspan class="TextParagraph" font-family="Liberation Sans, sans-serif" font-size="388px" font-weight="700"><tspan class="TextPosition" x="3955" y="955"><tspan fill="rgb(51,51,51)" stroke="none" style="white-space: pre"></tspan><tspan fill="rgb(51,51,51)" stroke="none" style="white-space: pre">//” </tspan><tspan font-style="italic" font-weight="400" fill="rgb(51,51,51)" stroke="none" style="white-space: pre">authority</tspan></tspan></tspan></text>
</g>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

View File

@@ -1,22 +1,29 @@
* xref:quicklook.adoc[Quick Look]
* xref:urls/index.adoc[URLs]
** xref:urls/parsing.adoc[Parsing]
** xref:urls/containers.adoc[Containers]
** xref:urls/segments.adoc[Segments]
** xref:urls/params.adoc[Params]
** xref:urls/normalization.adoc[Normalization]
** xref:urls/stringtoken.adoc[String Token]
** xref:urls/percent-encoding.adoc[Percent Encoding]
** xref:urls/formatting.adoc[Formatting]
* xref:grammar/index.adoc[Grammar]
** xref:grammar/rules.adoc[Parse Rules]
** xref:grammar/charset.adoc[Character Sets]
** xref:grammar/combinators.adoc[Compound Rules]
** xref:grammar/range.adoc[Ranges]
** xref:grammar/rfc3986.adoc[RFC 3986]
* xref:concepts/index.adoc[Concepts]
** xref:concepts/CharSet.adoc[CharSet]
** xref:concepts/Rule.adoc[Rule]
** xref:concepts/StringToken.adoc[StringToken]
* xref:examples.adoc[Examples]
* xref:HelpCard.adoc[Help Card]
* xref:quicklook.adoc[]
* xref:urls/index.adoc[]
** xref:urls/parsing.adoc[]
** xref:urls/containers.adoc[]
** xref:urls/segments.adoc[]
** xref:urls/params.adoc[]
** xref:urls/normalization.adoc[]
** xref:urls/stringtoken.adoc[]
** xref:urls/percent-encoding.adoc[]
** xref:urls/formatting.adoc[]
* xref:grammar/index.adoc[]
** xref:grammar/rules.adoc[]
** xref:grammar/charset.adoc[]
** xref:grammar/combinators.adoc[]
** xref:grammar/range.adoc[]
** xref:grammar/rfc3986.adoc[]
* Concepts
** xref:concepts/CharSet.adoc[]
** xref:concepts/Rule.adoc[]
** xref:concepts/StringToken.adoc[]
* Examples
** xref:examples/qrcode.adoc[]
** xref:examples/finicky.adoc[]
** xref:examples/mailto.adoc[]
** xref:examples/magnet-link.adoc[]
** xref:examples/file-router.adoc[]
** xref:examples/router.adoc[]
** xref:examples/sanitize.adoc[]
* xref:HelpCard.adoc[]

View File

@@ -8,8 +8,9 @@
//
// [section:helpcard Help Card]
[#helpcard]
= Help Card
// [$url/images/HelpCard.svg]
image:HelpCard.svg[]

View File

@@ -16,125 +16,211 @@
These member functions are available for interacting
with the scheme component of URL containers:
// [table Scheme Members [
// [Name]
// [Description]
// ][
// [[link url.ref.boost__urls__url_view_base.has_scheme `has_scheme`]]
// [
// Return `true` if a scheme is present.
// ]
// ][
// [[link url.ref.boost__urls__url_view_base.scheme `scheme`]]
// [
// Return the scheme as a string.
// ]
// ][
// [[link url.ref.boost__urls__url_view_base.scheme_id `scheme_id`]]
// [
// Return the scheme as a
// [link url.ref.boost__urls__scheme known scheme]
// constant,
// [link url.ref.boost__urls__scheme `scheme::unknown`]
// if the scheme is not well-known, or
// [link url.ref.boost__urls__scheme `scheme::none`]
// if no scheme is present.
// ]
// ][
// [[link url.ref.boost__urls__url_base.remove_scheme `remove_scheme`]]
// [
// Remove the scheme if present.
// ]
// ][
// [[link url.ref.boost__urls__url_base.set_scheme `set_scheme`]]
// [
// Set the scheme to a given string or
// [link url.ref.boost__urls__scheme known scheme]
// constant.
// ]
// ]]
[cols="a,a"]
|===
// Headers
|Name|Description
// Row 1, Column 1
|`has_scheme`
// Row 1, Column 2
|
// Row 1, Column 3
|Return `true` if a scheme is present.
// Row 1, Column 4
|
// Row 2, Column 1
|`scheme`
// Row 2, Column 2
|
// Row 2, Column 3
|Return the scheme as a string.
// Row 2, Column 4
|
// Row 3, Column 1
|`scheme_id`
// Row 3, Column 2
|
// Row 3, Column 3
|Return the scheme as a
known scheme
constant,
`scheme::unknown`
if the scheme is not well-known, or
`scheme::none`
if no scheme is present.
// Row 3, Column 4
|
// Row 4, Column 1
|`remove_scheme`
// Row 4, Column 2
|
// Row 4, Column 3
|Remove the scheme if present.
// Row 4, Column 4
|
// Row 5, Column 1
|`set_scheme`
// Row 5, Column 2
|
// Row 5, Column 3
|Set the scheme to a given string or
known scheme
constant.
// Row 5, Column 4
|
|===
// [table Userinfo Members [
// [Name]
// [Description]
// ][
// [[link url.ref.boost__urls__url_view_base.has_userinfo `has_userinfo`]]
// [
// Return `true` if a userinfo is present.
// ]
// ][
// [[link url.ref.boost__urls__url_view_base.encoded_userinfo `encoded_userinfo`]]
// [
// Return the userinfo field as a percent-encoded string.
// ]
// ][
// [[link url.ref.boost__urls__url_view_base.userinfo `userinfo`]]
// [
// Return the userinfo field with percent-decoding applied.
// ]
// ][
// [[link url.ref.boost__urls__url_view_base.encoded_user `encoded_user`]]
// [
// Return the user field as a percent-encoded string.
// ]
// ][
// [[link url.ref.boost__urls__url_view_base.user `user`]]
// [
// Return the user field with percent-decoding applied.
// ]
// ][
// [[link url.ref.boost__urls__url_view_base.has_password `has_password`]]
// [
// Return `true` if a password is present.
// ]
// ][
// [[link url.ref.boost__urls__url_view_base.encoded_password `encoded_password`]]
// [
// Return the password as a percent-encoded string.
// ]
// ][
// [[link url.ref.boost__urls__url_view_base.password `password`]]
// [
// Return the password with percent-decoding applied.
// ]
// ][
// [[link url.ref.boost__urls__url_base.set_user `set_user`]]
// [
// Set the user field using a plain string.
// ]
// ][
// [[link url.ref.boost__urls__url_base.remove_password `remove_password`]]
// [
// Remove the password field if present.
// ]
// ][
// [[link url.ref.boost__urls__url_base.set_encoded_password `set_encoded_password`]]
// [
// Set the password field using a percent-encoded string.
// ]
// ][
// [[link url.ref.boost__urls__url_base.set_password `set_password`]]
// [
// Set the password field using a plain string.
// ]
// ][
// [[link url.ref.boost__urls__url_base.remove_userinfo `remove_userinfo`]]
// [
// Remove the entire userinfo if present.
// ]
// ][
// [[link url.ref.boost__urls__url_base.set_encoded_userinfo `set_encoded_userinfo`]]
// [
// Set the entire userinfo using a percent-encoded string.
// ]
// ][
// [[link url.ref.boost__urls__url_base.set_userinfo `set_userinfo`]]
// [
// Set the entire userinfo using a plain string.
// ]
// ]]
[cols="a,a"]
|===
// Headers
|Name|Description
// Row 1, Column 1
|`has_userinfo`
// Row 1, Column 2
|
// Row 1, Column 3
|Return `true` if a userinfo is present.
// Row 1, Column 4
|
// Row 2, Column 1
|`encoded_userinfo`
// Row 2, Column 2
|
// Row 2, Column 3
|Return the userinfo field as a percent-encoded string.
// Row 2, Column 4
|
// Row 3, Column 1
|`userinfo`
// Row 3, Column 2
|
// Row 3, Column 3
|Return the userinfo field with percent-decoding applied.
// Row 3, Column 4
|
// Row 4, Column 1
|`encoded_user`
// Row 4, Column 2
|
// Row 4, Column 3
|Return the user field as a percent-encoded string.
// Row 4, Column 4
|
// Row 5, Column 1
|`user`
// Row 5, Column 2
|
// Row 5, Column 3
|Return the user field with percent-decoding applied.
// Row 5, Column 4
|
// Row 6, Column 1
|`has_password`
// Row 6, Column 2
|
// Row 6, Column 3
|Return `true` if a password is present.
// Row 6, Column 4
|
// Row 7, Column 1
|`encoded_password`
// Row 7, Column 2
|
// Row 7, Column 3
|Return the password as a percent-encoded string.
// Row 7, Column 4
|
// Row 8, Column 1
|`password`
// Row 8, Column 2
|
// Row 8, Column 3
|Return the password with percent-decoding applied.
// Row 8, Column 4
|
// Row 9, Column 1
|`set_user`
// Row 9, Column 2
|
// Row 9, Column 3
|Set the user field using a plain string.
// Row 9, Column 4
|
// Row 10, Column 1
|`remove_password`
// Row 10, Column 2
|
// Row 10, Column 3
|Remove the password field if present.
// Row 10, Column 4
|
// Row 11, Column 1
|`set_encoded_password`
// Row 11, Column 2
|
// Row 11, Column 3
|Set the password field using a percent-encoded string.
// Row 11, Column 4
|
// Row 12, Column 1
|`set_password`
// Row 12, Column 2
|
// Row 12, Column 3
|Set the password field using a plain string.
// Row 12, Column 4
|
// Row 13, Column 1
|`remove_userinfo`
// Row 13, Column 2
|
// Row 13, Column 3
|Remove the entire userinfo if present.
// Row 13, Column 4
|
// Row 14, Column 1
|`set_encoded_userinfo`
// Row 14, Column 2
|
// Row 14, Column 3
|Set the entire userinfo using a percent-encoded string.
// Row 14, Column 4
|
// Row 15, Column 1
|`set_userinfo`
// Row 15, Column 2
|
// Row 15, Column 3
|Set the entire userinfo using a plain string.
// Row 15, Column 4
|
|===

View File

@@ -8,19 +8,17 @@
//
// [section:charset CharSet]
[#charset]
= CharSet
A __CharSet__ is a unary predicate which is invocable with
this equivalent signature:
A __CharSet__ is a unary predicate which is invocable with this equivalent signature:
[source,cpp]
----
bool( char ch ) const noexcept;
----
The predicate returns `true` if `ch` is a member of the
set, or `false` otherwise.
The predicate returns `true` if `ch` is a member of the set, or `false` otherwise.
== Related Identifiers
@@ -37,74 +35,90 @@ In this table:
* `c` is a value of type `char`
* `first`, `last` are values of type `char const*`
// [table Valid expressions
// [[Expression] [Type] [Semantics, Pre/Post-conditions]]
// [
// [`t(c)`]
// [`bool`]
// [
// This function returns `true` if `c` is a member of
// the character set, otherwise it returns `false`.
// ]
// ][
// [
// ```
// t.find_if(first,last)
// ```
// ]
// [`char const*`]
// [
// This optional member function examines the valid
// range of characters in `[first, last)` and returns
// a pointer to the first occurrence of a character
// which is in the set, or returns `last` if no such
// character.
//
// The implementation of
// [link url.ref.boost__urls__grammar__find_if `find_if`]
// calls this function if provided by the character
// set, allowing optimized or otherwise performant
// implementations to be developed. If this member
// function is not provided, a default implementation
// is used which calls `operator()`.
// ]
// ][
// [
// ```
// t.find_if_not(first,last)
// ```
// ]
// [`char const*`]
// [
// This optional member function examines the valid
// range of characters in `[first, last)` and returns
// a pointer to the first occurrence of a character
// which is not in the set, or returns `last` if no
// such character.
//
// The implementation of
// [link url.ref.boost__urls__grammar__find_if_not `find_if_not`]
// calls this function if provided by the character
// set, allowing optimized or otherwise performant
// implementations to be developed. If this member
// function is not provided, a default implementation
// is used which calls `operator()`.
// ]
// ]]
//
// [heading Exemplar]
//
// For best results, it is suggested that all constructors and
// member functions for character sets be marked `constexpr`.
//
// [code_charset_1]
//
// [heading Models]
//
// * [link url.ref.boost__urls__grammar__alnum_chars `alnum_chars`]
// * [link url.ref.boost__urls__grammar__alpha_chars `alpha_chars`]
// * [link url.ref.boost__urls__grammar__digit_chars `digit_chars`]
// * [link url.ref.boost__urls__grammar__hexdig_chars `hexdig_chars`]
// * [link url.ref.boost__urls__grammar__lut_chars `lut_chars`]
//
// [endsect]
[cols="a,a,a"]
|===
// Headers
|Expression|Type|Semantics, Pre/Post-conditions
// Row 1, Column 1
|`t(c)`
// Row 1, Column 2
|`bool`
// Row 1, Column 3
|This function returns `true` if `c` is a member of
the character set, otherwise it returns `false`.
// Row 2, Column 1
|
[source,cpp]
----
t.find_if(first,last)
----
// Row 2, Column 2
|`char const*`
// Row 2, Column 3
|This optional member function examines the valid range of characters in `// [first, last)` and returns
a pointer to the first occurrence of a character
which is in the set, or returns `last` if no such
character.
The implementation of `grammar::find_if`
calls this function if provided by the character
set, allowing optimized or otherwise performant
implementations to be developed. If this member
function is not provided, a default implementation
is used which calls `operator()`.
// Row 3, Column 1
|
[source,cpp]
----
t.find_if_not(first,last)
----
// Row 3, Column 2
|`char const*`
// Row 3, Column 3
|This optional member function examines the valid
range of characters in `[first, last)` and returns
a pointer to the first occurrence of a character
which is not in the set, or returns `last` if no
such character.
The implementation of `grammar::find_if_not`
calls this function if provided by the character
set, allowing optimized or otherwise performant
implementations to be developed. If this member
function is not provided, a default implementation
is used which calls `operator()`.
|===
== Exemplar
For best results, it is suggested that all constructors and
member functions for character sets be marked `constexpr`.
// code_charset_1
[source,cpp]
----
struct CharSet
{
bool operator()( char c ) const noexcept;
// These are both optional. If either or both are left
// unspecified, a default implementation will be used.
//
char const* find_if( char const* first, char const* last ) const noexcept;
char const* find_if_not( char const* first, char const* last ) const noexcept;
};
----
== Models
* `grammar::alnum_chars`
* `grammar::alpha_chars`
* `grammar::digit_chars`
* `grammar::hexdig_chars`
* `grammar::lut_chars`

View File

@@ -8,7 +8,7 @@
//
== Rule
= Rule
A __Rule__ defines an algorithm used to match an input buffer of
ASCII characters against a set of syntactical specifications.
@@ -18,11 +18,11 @@ rules for productions typically found in RFC documents. Rules
are not invoked directly; instead, rule variables are used with
overloads of `parse` which provide a convenient, uniform front end.
=== Related Identifiers
== Related Identifiers
`is_rule`, `parse`.
=== Requirements
== Requirements
In this table:
@@ -31,79 +31,84 @@ In this table:
* `it` is an __lvalue__ with type `char const*`
* `end` is a value of type `char const*`
// [table Valid expressions
// [[Expression] [Type] [Semantics, Pre/Post-conditions]]
// [
// [
// ```
// T(t)
// ```
// ]
// []
// [
// Copy construction of `T` throws nothing.
//
// `std::is_nothrow_copy_constructible<T>::value == true`
// ]
// ][
// [
// ```
// T::value_type
// ```
// ]
// []
// [
// Values of this type are returned by the rule when the
// parse operation is successful
// ]
// ][
// [
// ```
// t.parse(it,end)
// ```
// ]
// [`result<T::value_type>`]
// [
// Attempt to parse the buffer of characters defined by
// the range `[it,end)`. Upon success, the return result
// holds an instance of the rule's value type, and
// the reference parameter `it` is modified to point
// to the first unconsumed character. Otherwise, upon
// failure the result holds an error. In this case
// the implementation defines if and how the reference
// parameter `it` is modified.
// ]
// ]]
//
// [heading Exemplar]
//
// For best results, it is suggested that all constructors for
// rules be marked `constexpr`.
//
// ```
// struct Rule
// {
// struct value_type;
//
// constexpr Rule( Rule const& ) noexcept = default;
//
// auto parse( char const*& it, char const* end ) const -> result< value_type >;
// };
//
// // Declare a variable of type Rule for notational convenience
// constexpr Rule rule{};
// ```
//
// [heading Models]
//
// * __dec_octet_rule__
// * __delim_rule__
// * __not_empty_rule__
// * __optional_rule__
// * __range_rule__
// * __token_rule__
// * __tuple_rule__
// * __unsigned_rule__
// * __variant_rule__
//
// [endsect]
[cols="a,a,a"]
|===
// Headers
|Expression|Type|Semantics, Pre/Post-conditions
// Row 1, Column 1
|[source,cpp]
----
T(t)
----
// Row 1, Column 2
| -
// Row 1, Column 3
|Copy construction of `T` throws nothing.
`std::is_nothrow_copy_constructible<T>::value == true`
// Row 2, Column 1
|[source,cpp]
----
T::value_type
----
// Row 2, Column 2
| -
// Row 2, Column 3
|Values of this type are returned by the rule when the
parse operation is successful
// Row 3, Column 1
|[source,cpp]
----
t.parse(it,end)
----
// Row 3, Column 2
|`result<T::value_type>`
// Row 3, Column 3
|Attempt to parse the buffer of characters defined by
the range `// [it,end)`. Upon success, the return result
holds an instance of the rule's value type, and
the reference parameter `it` is modified to point
to the first unconsumed character. Otherwise, upon
failure the result holds an error. In this case
the implementation defines if and how the reference
parameter `it` is modified.
|===
== Exemplar
For best results, it is suggested that all constructors for
rules be marked `constexpr`.
[source,cpp]
----
struct Rule
{
struct value_type;
constexpr Rule( Rule const& ) noexcept = default;
auto parse( char const*& it, char const* end ) const -> result< value_type >;
};
// Declare a variable of type Rule for notational convenience
constexpr Rule rule{};
----
== Model
* `grammar::dec_octet_rule`
* `grammar::delim_rule`
* `grammar::not_empty_rule`
* `grammar::optional_rule`
* `grammar::range_rule`
* `grammar::token_rule`
* `grammar::tuple_rule`
* `grammar::unsigned_rule`
* `grammar::variant_rule`

View File

@@ -8,7 +8,7 @@
//
== StringToken
= StringToken
A string token is an rvalue passed to a function template which
customizes the return type of the function and also controls how
@@ -18,7 +18,7 @@ function call in which it appears as a parameter. A string
token cannot be copied, moved, or assigned, and must be
destroyed when the function returns or throws.
=== Requirements
== Requirements
In this table:
@@ -26,73 +26,81 @@ In this table:
* `t` is an rvalue reference of type T
* `n` is a value of type `std::size_t`
// [table Valid expressions
// [[Expression] [Result] [Semantics, Pre/Post-conditions]]
// [
// [
// ```
// std::derived_from<T,string_token::arg>
// ```
// ]
// [
// ```
// true
// ```
// ]
// [
// All string tokens must be publicly and
// unambiguously derived from
// [link url.ref.boost__urls__string_token__arg `string_token::arg`].
// ]
// ][
// [
// ```
// T::result_type
// ```
// ]
// []
// [
// This type determines the return type of functions
// which accept a string token.
// ]
// ][
// [
// ```
// t.prepare(n);
// ```
// ]
// [
// ```
// char*
// ```
// ]
// [
// This function overrides the virtual function in the base.
// It must return a pointer to a character buffer of at least
// size `n`, otherwise throw an exception.
// ]
// ][
// [
// ```
// t.result();
// ```
// ]
// [
// ```
// T::result_type
// ```
// ]
// [
// This function is invoked by the algorithm to receive the result
// from the string token.
// It is only invoked if `prepare` returned successfuly and the
// string token was not destroyed.
// ]
// ]]
[cols="a,a,a"]
|===
// Headers
|Expression|Result|Semantics, Pre/Post-conditions
// Row 1, Column 1
|[source,cpp]
----
std::derived_from<T,string_token::arg>
----
// Row 1, Column 2
|[source,cpp]
----
true
----
// Row 1, Column 3
|All string tokens must be publicly and
unambiguously derived from
`string_token::arg`.
// Row 2, Column 1
|[source,cpp]
----
T::result_type
----
// Row 2, Column 2
|
// Row 2, Column 3
|This type determines the return type of functions
which accept a string token.
// Row 3, Column 1
|[source,cpp]
----
t.prepare(n);
----
// Row 3, Column 2
|[source,cpp]
----
char*
----
// Row 3, Column 3
|This function overrides the virtual function in the base.
It must return a pointer to a character buffer of at least
size `n`, otherwise throw an exception.
// Row 4, Column 1
|[source,cpp]
----
t.result();
----
// Row 4, Column 3
|[source,cpp]
----
T::result_type
----
// Row 4, Column 5
|This function is invoked by the algorithm to receive the result
from the string token.
It is only invoked if `prepare` returned successfuly and the
string token was not destroyed.
|===
=== Algorithm Requirements
== Algorithm Requirements
When an algorithm accepts a string token, it must meet these requirements:
@@ -105,7 +113,7 @@ String tokens cannot be reused.
=== Exemplars
== Exemplars
String token prototype:
@@ -166,9 +174,9 @@ algorithm( StringToken&& token = {} ) ->
Models
* `append_to`
* `assign_to`
* `preserve_size`
* `append_to`
* `string_token::return_string`
* `string_token::assign_to`
* `string_token::preserve_size`
* `string_token::return_string`

View File

@@ -8,12 +8,12 @@
//
== Concepts
= Concepts
This section describes all of the concepts defined by the library.
// [include 5.1.CharSet.qbk]
// [include 5.2.Rule.qbk]
// [include 5.3.StringToken.qbk]

View File

@@ -1,144 +0,0 @@
//
// Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/url
//
== Examples
=== QR Code
A QR code is a machine-readable two-dimensional barcode. They might contain data
for a identifier or a URL to a website.
This example shows how to construct and modify URLs to consume a third party API to
generate QR Codes.
[source,cpp]
----
// example_qrcode
----
=== Finicky
This example shows how to classify URLs according to a set of rules. It is
inspired by https://github.com/johnste/finicky[Finicky,window=blank_] application.
The URLs are classified and redirected to a browser according to their
category. See the example `config.json` file.
[source,cpp]
----
// example_finicky
----
=== mailto URLs
`mailto` is a URL scheme for email addresses. `mailto` URL are used on websites
to allow users to send an email to a specific address directly from an HTML document.
This example parses a mailto URL into a new view type and prints its components to
standard output.
[source,cpp]
----
// example_mailto
----
=== Magnet Link
`magnet` is a URL scheme for identifying files by their content. These files are
usually identified by cryptographic hash value.
Magnet links are useful in peer-to-peer file sharing networks because they allow
resources to be referred to without the need for a continuously available host..
This example parses a magnet link into a new view type and prints its components to
standard output.
[source,cpp]
----
// example_magnet
----
=== File Router
This example defines a router that associates URL paths to a directory in the filesystem. If
the specified route matches and the file exists, the example prints its contents to standard output.
[source,cpp]
----
// example_file_router
----
=== Router
This example defines a router for URL paths. If the specified route matches one of the existing
routes, the example executes the underlying callback function.
[source,cpp]
----
// example_router
----
=== Sanitizing URLs
This example parses a non-strict or invalid URL
into path components according to its delimiters.
This pattern can be adapted to the requirements of other
applications.
Once the non-strict components are determined, a new URL is
created and its parts are set with the `set_encoded_X`
functions, which will encode any invalid chars accordingly.
This sort of transformation is useful in applications that are
extremely loose in what kinds of URLs they accept, such as
browsers. The sanitized URL can later be used for machine-to-machine
communication.
Using non-strict URLs directly is a security concern in
machine-to-machine communication, is ambiguous, and also
involve an extra cost for the transformations.
Different transformations are required by different applications to
construct a valid URL appropriate for machine-to-machine communication.
For instance, if an invalid relative reference includes something that
looks like a host in the first path segment, browsers usually interpret
that as the host with an implicit "https" scheme. Other applications
also have other implicit schemes.
The example also identifies whether the input url is already valid.
It includes diagnostics that can be used to help the user determine
if a URL is invalid and why it's invalid.
Once all transformations are applied, the result is a URL
appropriate for machine-to-machine communication.
[source,cpp]
----
// example_sanitize_url
----

View File

@@ -0,0 +1,221 @@
//
// Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/url
//
= File Router
This example defines a router that associates URL paths to a directory in the filesystem. If
the specified route matches and the file exists, the example prints its contents to standard output.
// example_file_router
[source,cpp]
----
/*
This example defines a route for a URL path.
If the specified route matches and the file
exists, the example prints its contents to
standard output.
*/
#include <boost/url/error.hpp>
#include <boost/url/parse.hpp>
#include <boost/url/segments_encoded_ref.hpp>
#include <boost/url/segments_encoded_view.hpp>
#include <boost/url/string_view.hpp>
#include <boost/url/url.hpp>
#include <boost/url/url_view.hpp>
#include <boost/url/static_url.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <iostream>
namespace urls = boost::urls;
namespace fs = boost::filesystem;
namespace core = boost::core;
using string_view = boost::core::string_view;
/** Check if a target matches a prefix
This function checks if the first segments
of the target match the corresponding prefix
segments.
@param target Target segments
@param prefix Prefix segments
@return True if target matches prefix
*/
bool match_prefix(
urls::segments_view target,
urls::segments_view prefix)
{
// Trivially reject target that cannot
// contain the prefix
if (target.size() < prefix.size())
return false;
// Match the prefix segments
auto it0 = target.begin();
auto end0 = target.end();
auto it1 = prefix.begin();
auto end1 = prefix.end();
while (
it0 != end0 &&
it1 != end1 &&
*it0 == *it1)
{
++it0;
++it1;
}
return it1 == end1;
}
/** A static route representing files in a directory
A route is a URL logical prefix representing
static files in the specified root directory.
The `match` function returns the corresponding
file for a given URL path.
*/
class route
{
public:
/// Constructor
route(core::string_view prefix, fs::path root)
: prefix_(urls::parse_uri_reference(prefix).value())
, root_(std::move(root))
{}
/// Constructor
route(urls::url prefix, fs::path root)
: prefix_(std::move(prefix))
, root_(std::move(root))
{}
/** Match target URL path with a file
This function attempts to match the target
URL path with the route prefix.
If the prefix matches, the target is
considered to represent a file in the root
directory. When that happens, the target
prefix is consumed and other segments are
appended to the root path.
The complete file path represented by the
target is returned as the output parameter
`result`.
@param target Target URL path
@param result An out-parameter holding the resulting path
@return `true` if target matches the directory
*/
bool match(
urls::url_view target,
fs::path& result)
{
if (match_prefix(
target.segments(),
static_cast<urls::url_view>(prefix_).segments()))
{
result = root_;
auto segs = target.segments();
auto it = segs.begin();
auto end = segs.end();
std::advance(it, prefix_.segments().size());
while (it != end)
{
auto seg = *it;
result.append(seg.begin(), seg.end());
++it;
}
return true;
}
return false;
}
private:
urls::url prefix_;
fs::path root_;
};
int
main(int argc, char **argv)
{
namespace urls = boost::urls;
namespace fs = boost::filesystem;
// Check command line arguments.
if (argc != 4)
{
fs::path exec = argv[0];
exec = exec.filename();
std::cerr
<< "Usage: " << exec
<< " <target> <prefix> <doc_root>\n"
"target: path to make a request\n"
"prefix: url prefix\n"
"doc_root: dir to look for files\n";
return EXIT_FAILURE;
}
try {
urls::url target =
urls::parse_uri_reference(argv[1]).value();
target.normalize_path();
std::string prefix = argv[2];
fs::path root = argv[2];
if (!fs::is_directory(root))
{
std::cerr
<< "Error: " << root
<< " is not a directory\n";
return EXIT_FAILURE;
}
// Create route
route r(prefix, root);
// Check if target matches a file
fs::path result;
if (r.match(target, result))
{
fs::ifstream f(result);
std::string l;
while (std::getline(f, l))
std::cout << l << '\n';
f.close();
}
else
{
std::cout
<< "No " << target << " in prefix "
<< prefix << std::endl;
}
return EXIT_SUCCESS;
}
catch (std::exception &e)
{
std::cerr << e.what() << "\n";
return EXIT_FAILURE;
}
}
----

View File

@@ -0,0 +1,413 @@
//
// Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/url
//
= Finicky
This example shows how to classify URLs according to a set of rules. It is
inspired by https://github.com/johnste/finicky[Finicky,window=blank_] application.
The URLs are classified and redirected to a browser according to their
category. See the example `config.json` file.
// example_finicky
[source,cpp]
----
/*
This example shows how to classify URLs
according to a set of rules. This example is
inspired by Finicky. The URLs are classified
and redirected to a browser according to their
category. See the example config.json file.
https://github.com/johnste/finicky
*/
#include <boost/url/url.hpp>
#include <boost/url/parse.hpp>
#include <boost/system/result.hpp>
#include <boost/json/stream_parser.hpp>
#include <boost/core/detail/string_view.hpp>
#include <boost/regex.hpp>
#include <iostream>
#include <fstream>
namespace urls = boost::urls;
namespace json = boost::json;
namespace core = boost::core;
json::value
read_json( std::istream& is, json::error_code& ec )
{
json::parse_options opt;
opt.allow_comments = true;
json::stream_parser p(json::storage_ptr(), opt);
std::string line;
while( std::getline( is, line ) )
{
p.write( line, ec );
if( ec )
return nullptr;
}
p.finish( ec );
if( ec )
return nullptr;
return p.release();
}
bool
glob_match(
core::string_view pattern,
core::string_view str)
{
// regex
if (str.starts_with("/") &&
str.ends_with("/"))
{
const boost::regex pr(pattern.begin() + 1, pattern.end() - 1);
return boost::regex_match(std::string(str), pr);
}
// literal
if (!pattern.contains('*'))
{
return pattern == str;
}
// glob
std::string p = pattern;
std::size_t i = p.find('*');
while (i != std::string::npos)
{
auto e = std::min(p.find_first_not_of('*', i), p.size());
std::size_t n = e - i;
if (n == 1)
{
p.replace(i, e, "[^/]*");
i += 5;
}
else
{
p.replace(i, e, ".*");
i += 2;
}
i = p.find('*', i);
}
const boost::regex pr(p);
return boost::regex_match(std::string(str), pr);
}
bool
url_match(
json::value& mv,
urls::url const& u)
{
if (mv.is_string())
{
json::string& p = mv.as_string();
return glob_match(u.buffer(), p);
}
else if (mv.is_array())
{
json::array& m = mv.as_array();
for (auto& mi: m)
{
if (!mi.is_string())
throw std::invalid_argument(
"handle match is not a string");
if (glob_match(mi.as_string(), u.buffer()))
return true;
}
}
else if (mv.is_object())
{
json::object& m = mv.as_object();
std::pair<core::string_view, core::string_view>
field_values[] = {
{"protocol", u.scheme()},
{"authority", u.encoded_authority()},
{"username", u.encoded_user()},
{"user", u.encoded_user()},
{"password", u.encoded_password()},
{"userinfo", u.encoded_userinfo()},
{"host", u.encoded_host()},
{"port", u.port()},
{"path", u.encoded_path()},
{"pathname", u.encoded_path()},
{"query", u.encoded_query()},
{"search", u.encoded_query()},
{"fragment", u.encoded_fragment()},
{"hash", u.encoded_fragment()},
};
for (auto& p: field_values)
{
auto it = m.find(p.first);
if (it != m.end())
{
if (!it->value().is_string())
throw std::invalid_argument(
"match fields should be a strings");
if (glob_match(p.second, p.first))
return true;
}
}
}
return false;
}
#define CHECK(c, msg) \
if (!(c)) \
{ \
std::cerr << msg << "\n"; \
return EXIT_FAILURE; \
}
int main(int argc, char** argv)
{
if (argc < 3) {
std::cout << argv[0] << "\n";
std::cout << "Usage: finicky <config> <url>\n"
"options:\n"
" <config>: Configuration file\n"
" <url>: The url to open\n"
"examples:\n"
" finicky config.json \"http://www.example.com\"\n";
return EXIT_FAILURE;
}
// Parse url
boost::system::result<urls::url> ru = urls::parse_uri(argv[2]);
CHECK(ru, "Invalid URL");
urls::url u = *ru;
// Open config file
std::fstream fin(argv[1]);
CHECK(fin.good(), "Cannot open configuration file");
json::error_code ec;
json::value c = read_json(fin, ec);
CHECK(!ec.failed(), "Cannot parse configuration file");
CHECK(c.is_object(), "Configuration file is not an object");
json::object& o = c.as_object();
// Set initial browser
auto bit = o.find("defaultBrowser");
CHECK(
bit != o.end(),
"Configuration file has no defaultBrowser");
CHECK(
bit->value().is_string(),
"defaultBrowser should be a string");
json::string& browser = bit->value().as_string();
// Apply rewrites to the input string
auto rsit = o.find("rewrite");
if (rsit != o.end())
{
CHECK(
rsit->value().is_array(),
"rewrite rules should be an array");
auto& rs = rsit->value().as_array();
for (auto& rv: rs)
{
CHECK(
rv.is_object(),
"individual rewrite rule should be an object");
json::object& r = rv.as_object();
// Look for match
auto mit = r.find("match");
CHECK(
mit != r.end(),
"rewrite rule should have a match field");
CHECK(
mit->value().is_object() || mit->value().is_string(),
"rewrite match field is not an object");
if (!url_match(mit->value(), u))
continue;
// Apply replacement rule
auto uit = r.find("url");
CHECK(
uit != r.end(),
"rewrite rule should have a url field");
CHECK(
uit->value().is_object() ||
uit->value().is_string(),
"url field must be an object or string");
if (uit->value().is_string())
{
json::string& uo = uit->value().as_string();
auto ru1 = urls::parse_uri(uo);
CHECK(ru1, "url " << uo.c_str() << " is invalid");
u = *ru;
}
else
{
json::object& uo = uit->value().as_object();
auto it = uo.find("protocol");
if (it != uo.end())
{
CHECK(
it->value().is_string(),
"protocol field should be a string");
u.set_scheme(it->value().as_string());
}
it = uo.find("authority");
if (it != uo.end())
{
CHECK(
it->value().is_string(),
"authority field should be a string");
u.set_encoded_authority(
it->value().as_string().subview());
}
it = uo.find("username");
if (it == uo.end())
it = uo.find("user");
if (it != uo.end())
{
CHECK(
it->value().is_string(),
"username field should be a string");
u.set_encoded_user(
it->value().as_string().subview());
}
it = uo.find("password");
if (it != uo.end())
{
CHECK(
it->value().is_string(),
"password field should be a string");
u.set_encoded_password(
it->value().as_string().subview());
}
it = uo.find("userinfo");
if (it != uo.end())
{
CHECK(
it->value().is_string(),
"userinfo field should be a string");
u.set_encoded_userinfo(
it->value().as_string().subview());
}
it = uo.find("host");
if (it != uo.end())
{
CHECK(
it->value().is_string(),
"host field should be a string");
u.set_encoded_host(
it->value().as_string().subview());
}
it = uo.find("port");
if (it != uo.end())
{
CHECK(
it->value().is_string(),
"port field should be a string");
u.set_port(
it->value().as_string().subview());
}
it = uo.find("path");
if (it == uo.end())
it = uo.find("pathname");
if (it != uo.end())
{
CHECK(
it->value().is_string(),
"path field should be a string");
u.set_encoded_path(
it->value().as_string().subview());
}
it = uo.find("query");
if (it == uo.end())
it = uo.find("search");
if (it != uo.end())
{
CHECK(
it->value().is_string(),
"query field should be a string");
u.set_encoded_query(
it->value().as_string().subview());
}
it = uo.find("fragment");
if (it == uo.end())
it = uo.find("hash");
if (it != uo.end())
{
CHECK(
it->value().is_string(),
"fragment field should be a string");
u.set_encoded_fragment(
it->value().as_string().subview());
}
}
}
}
// Determine which browser should handle the url
auto hsit = o.find("handlers");
if (hsit != o.end())
{
CHECK(
hsit->value().is_array(),
"handler rules should be an array");
auto& hs = hsit->value().as_array();
for (auto& hv: hs)
{
CHECK(
hv.is_object(),
"individual handlers should be an object");
json::object& h = hv.as_object();
auto mit = h.find("match");
CHECK(
mit != h.end(),
"handle rule should have a match field");
CHECK(
mit->value().is_string() || mit->value().is_array(),
"handle match field must be an array or a string");
auto hbit = h.find("browser");
CHECK(
hbit != h.end(),
"handle rule should have a browser field");
CHECK(
hbit->value().is_string(),
"browser field is not a string");
// Look for match and change browser
if (url_match(mit->value(), u))
{
browser = hbit->value().as_string().subview();
break;
}
}
}
// Print command finicky would run
std::cout << "\"" << browser.c_str() << "\" " << u << '\n';
return EXIT_SUCCESS;
}
----

View File

@@ -0,0 +1,24 @@
//
// Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/url
//
= Examples

View File

@@ -0,0 +1,733 @@
//
// Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/url
//
= Magnet Link
`magnet` is a URL scheme for identifying files by their content. These files are
usually identified by cryptographic hash value.
Magnet links are useful in peer-to-peer file sharing networks because they allow
resources to be referred to without the need for a continuously available host..
This example parses a magnet link into a new view type and prints its components to
standard output.
// example_magnet
[source,cpp]
----
/*
This example parses a magnet link into a new
view type and prints its components to
standard output.
*/
#include <boost/url/url_view.hpp>
#include <boost/url/url.hpp>
#include <boost/url/optional.hpp>
#include <boost/url/parse.hpp>
#include <boost/url/pct_string_view.hpp>
#include <boost/url/rfc/absolute_uri_rule.hpp>
#include <boost/url/grammar/digit_chars.hpp>
#include <boost/url/grammar/parse.hpp>
#include <boost/core/detail/string_view.hpp>
#include "filter_view.hpp"
#include <iostream>
namespace urls = boost::urls;
namespace core = boost::core;
/** Callable to identify a magnet "exact topic"
This callable evaluates if a query parameter
represents a magnet "exact topic".
This callable is used as a filter for
the topics_view.
*/
struct is_exact_topic
{
bool
operator()(urls::param_view p);
};
/** Callable to identify a magnet url parameter
This callable evaluates if a query parameter
has a given key and a url as its value.
These urls are percent-encoded twice,
which means we need to decode it once
before attempting to parse it.
This callable is used as a filter for
the keys_view.
*/
class is_url_with_key
{
core::string_view k_;
public:
is_url_with_key(
core::string_view key)
: k_(key) {}
bool
operator()(urls::param_view p);
};
/** Callable to convert param values to urls
This callable converts the value of a
query parameter into a urls::url_view.
This callable is used as a transform
function for the topics_view.
*/
struct param_view_to_url
{
urls::url
operator()(urls::param_view p);
};
/** Callable to convert param values to std::string
This callable converts the value of a
query parameter into a std::string.
This callable is used as a transform
function for the keys_view.
*/
struct to_decoded_value
{
std::string
operator()(urls::param_view p)
{
return p.value;
}
};
/** Callable to convert param values to info_hashes
This callable converts the value of a
query parameter into a core::string_view with
its infohash.
The infohash hash is a parameter of an
exact topic field in the magnet link.
This callable is used as a transform
function for the info_hashes_view.
*/
struct param_view_to_infohash
{
core::string_view
operator()(urls::param_view p);
};
/** Callable to convert param values to protocols
This callable converts the value of a
query parameter into a core::string_view with
its protocol.
The protocol is a parameter of an exact
topic field in the magnet link.
This callable is used as a transform
function for the protocols_view.
*/
struct to_protocol
{
core::string_view
operator()(urls::param_view p);
};
struct magnet_link_rule_t;
/** A new url type for magnet links
This class represents a reference to a
magnet link.
Unlike a urls::url_view, which only represents the
general syntax of urls, a magnet_link_view
represents a reference to fields that are
relevant to magnet links, while ignoring
elements of the general syntax
that are not relevant to the scheme.
This allows us to use the general syntax
parsers to create a representation that
is more appropriate for the specified scheme
syntax.
@par Specification
@li <a href="https://www.bittorrent.org/beps/bep_0005.html"
>DHT Protocol</a>
@li <a href="https://www.bittorrent.org/beps/bep_0009.html"
>Extension for Peers to Send Metadata Files</a>
@li <a href="https://www.bittorrent.org/beps/bep_0053.html"
>Magnet URI extension</a>
@li <a href="https://en.wikipedia.org/wiki/Magnet_URI_scheme"
>Magnet URI scheme</a>
@par References
@li <a href="https://github.com/webtorrent/magnet-uri"
>magnet-uri</a>
*/
class magnet_link_view
{
urls::url_view u_;
public:
/// A view of all exact topics in the magnet_link
using topics_view =
filter_view<
urls::params_view,
urls::url,
is_exact_topic,
param_view_to_url>;
/// A view of all info_hashes in the magnet_link
using info_hashes_view =
filter_view<
urls::params_view,
std::string,
is_exact_topic,
param_view_to_infohash>;
/// A view of all protocols in the magnet_link
using protocols_view =
filter_view<
urls::params_view,
std::string,
is_exact_topic,
to_protocol>;
/** A view of all urls with the specified key in the magnet_link
A number of fields in a magnet link refer
to a list of urls with the same query
parameter keys.
*/
using keys_view =
filter_view<
urls::params_view,
std::string,
is_url_with_key,
to_decoded_value>;
/** URNs to the file or files hashes
An exact topic is the main field of a
magnet link. A magnet link must contain
one or more exact topics with the query
key "xt" or ["xt.1", "xt.2", ...].
The value of each exact topic is a URN
representing the file hash and the protocol
to access the file.
@return A view of all exact topic URNs in the link
*/
topics_view
exact_topics() const noexcept;
/** Info hash of the file or files
@return A view of all info hashes in exact topics
*/
info_hashes_view
info_hashes() const noexcept;
/** Protocol of the exact topics
@return A view of all protocols in exact topics
*/
protocols_view
protocols() const noexcept;
/** Return view of address trackers
A tracker URL is used to obtain resources
for BitTorrent downloads.
@return A view of all address trackers in the link
*/
keys_view
address_trackers() const;
/** Return view of exact sources
An exact source URL is a direct download
link to the file.
@return A view of all exact sources
*/
keys_view
exact_sources() const;
/** Return view of acceptable sources
An acceptable source URL is a direct
download link to the file that can be
used as a fallback for exact sources.
@return A view of all acceptable sources
*/
keys_view
acceptable_sources() const;
/** Return keyword topic
The keyword topic is the search keywords
to use in P2P networks.
@par Example
kt=martin+luther+king+mp3
@return Keyword topic
*/
boost::optional<std::string>
keyword_topic() const noexcept;
/** Return manifest topics
This function returns a link to the
metafile that contains a list of magneto.
@par Specification
@li <a href="http://rakjar.de/gnuticles/MAGMA-Specsv22.txt"
>MAGnet MAnifest</a>
@return A view of manifest topics
*/
keys_view
manifest_topics() const;
/** Return display name
This function returns a filename to
display to the user. This field is
only used for convenience.
@par Specification
@li <a href="http://rakjar.de/gnuticles/MAGMA-Specsv22.txt"
>MAGnet MAnifest</a>
@return Display name
*/
boost::optional<urls::pct_string_view>
display_name() const noexcept;
/** Return web seed
The web seed represents the payload data
served over HTTP(S).
@return Web seed
*/
keys_view
web_seed() const;
/** Return extra supplement parameter
This function returns informal options
and parameters of the magnet link.
Query parameters whose keys have the
prefix "x." are used in magnet links
for extra parameters. These names
are guaranteed to never be standardized.
@par Example
x.parameter_name=parameter_data
@return Web seed
*/
boost::optional<urls::pct_string_view>
param(core::string_view key) const noexcept;
friend
std::ostream&
operator<<(std::ostream& os, magnet_link_view m)
{
return os << m.u_;
}
private:
// get a query parameter as a urls::pct_string_view
boost::optional<urls::pct_string_view>
encoded_param(core::string_view key) const noexcept;
// get a query parameter as a urls::url_view
boost::optional<urls::url_view>
url_param(core::string_view key) const noexcept;
friend magnet_link_rule_t;
};
bool
is_exact_topic::
operator()(urls::param_view p)
{
// These comparisons use the lazy
// operator== for urls::pct_string_view
// For instance, the comparison also works
// if the underlying key is "%78%74"/
if (p.key == "xt")
return true;
return
p.key.size() > 3 &&
*std::next(p.key.begin(), 0) == 'x' &&
*std::next(p.key.begin(), 1) == 't' &&
*std::next(p.key.begin(), 2) == '.' &&
std::all_of(
std::next(p.key.begin(), 3),
p.key.end(),
urls::grammar::digit_chars);
}
bool
is_url_with_key::
operator()(urls::param_view p)
{
if (p.key != k_)
return false;
boost::system::error_code ec;
std::string buf(
p.value.begin(), p.value.end());
if (ec.failed())
return false;
boost::system::result<urls::url_view> r =
urls::parse_uri(buf);
return r.has_value();
}
urls::url
param_view_to_url::
operator()(urls::param_view p)
{
// `param_view_to_url` is used in topics_view,
// where the URL is not
// percent-encoded twice.
// Thus, we can already parse the
// encoded value.
auto ur =
urls::parse_uri(p.value);
BOOST_ASSERT(ur);
urls::url u = *ur;
return u;
}
core::string_view
param_view_to_infohash::
operator()(urls::param_view p)
{
urls::url_view topic =
urls::parse_uri(p.value).value();
core::string_view t = topic.encoded_path();
std::size_t pos = t.find_last_of(':');
if (pos != core::string_view::npos)
return t.substr(pos + 1);
return t;
}
core::string_view
to_protocol::
operator()(urls::param_view p)
{
urls::url_view topic =
urls::parse_uri(p.value).value();
core::string_view t = topic.encoded_path();
std::size_t pos = t.find_last_of(':');
return t.substr(0, pos);
}
auto
magnet_link_view::exact_topics() const noexcept
-> topics_view
{
return {u_.params()};
}
auto
magnet_link_view::info_hashes() const noexcept
-> info_hashes_view
{
return {u_.params()};
}
auto
magnet_link_view::protocols() const noexcept
-> protocols_view
{
return {u_.params()};
}
auto
magnet_link_view::address_trackers() const
-> keys_view
{
return {
u_.params(),
is_url_with_key{"tr"}};
}
auto
magnet_link_view::exact_sources() const
-> keys_view
{
return {
u_.params(),
is_url_with_key{"xs"}};
}
auto
magnet_link_view::acceptable_sources() const
-> keys_view
{
return {
u_.params(),
is_url_with_key{"as"}};
}
boost::optional<std::string>
magnet_link_view::keyword_topic() const noexcept
{
boost::optional<urls::pct_string_view> o =
encoded_param("kt");
if (o)
return o->decode();
return boost::none;
}
auto
magnet_link_view::manifest_topics() const
-> keys_view
{
return {
u_.params(),
is_url_with_key{"mt"}};
}
boost::optional<urls::pct_string_view>
magnet_link_view::display_name() const noexcept
{
return encoded_param("dn");
}
auto
magnet_link_view::web_seed() const
-> keys_view
{
return {
u_.params(),
is_url_with_key{"ws"}};
}
boost::optional<urls::pct_string_view>
magnet_link_view::param(core::string_view key) const noexcept
{
urls::params_view ps = u_.params();
auto it = ps.begin();
auto end = ps.end();
while (it != end)
{
urls::param_view p = *it;
if (p.key.size() < 2)
{
++it;
continue;
}
auto first = p.key.begin();
auto mid = std::next(p.key.begin(), 2);
auto last = p.key.end();
urls::pct_string_view prefix(
core::string_view(first, mid));
urls::pct_string_view suffix(
core::string_view(mid, last));
if (prefix == "x." &&
suffix == key &&
p.has_value)
return urls::pct_string_view(p.value);
++it;
}
return boost::none;
}
boost::optional<urls::pct_string_view>
magnet_link_view::encoded_param(core::string_view key) const noexcept
{
urls::params_encoded_view ps = u_.encoded_params();
auto it = ps.find(key);
if (it != ps.end() && (*it).has_value)
return urls::pct_string_view((*it).value);
return boost::none;
}
boost::optional<urls::url_view>
magnet_link_view::url_param(core::string_view key) const noexcept
{
urls::params_encoded_view ps = u_.encoded_params();
auto it = ps.find(key);
if (it != ps.end() && (*it).has_value)
{
boost::system::result<urls::url_view> r =
urls::parse_uri((*it).value);
if (r)
return *r;
}
return boost::none;
}
/** Rule to match a magnet link
*/
struct magnet_link_rule_t
{
/// Value type returned by the rule
using value_type = magnet_link_view;
/// Parse a sequence of characters into a magnet_link_view
boost::system::result< value_type >
parse( char const*& it, char const* end ) const noexcept;
};
auto
magnet_link_rule_t::parse(
char const*& it,
char const* end ) const noexcept
-> boost::system::result< value_type >
{
// 1) Parse url with the general uri syntax
boost::system::result<urls::url_view> r =
urls::grammar::parse(it, end, urls::absolute_uri_rule);
if(!r)
return urls::grammar::error::invalid;
magnet_link_view m;
m.u_ = *r;
// 2) Check if exact topics are valid urls
// and that we have at least one. This is the
// only mandatory field in magnet links.
auto ps = m.u_.params();
auto pit = ps.begin();
auto pend = ps.end();
pit = std::find_if(pit, pend, is_exact_topic{});
if (pit == pend)
{
// no exact topic in the magnet link
return urls::grammar::error::invalid;
}
// all topics should parse as valid urls
if (!std::all_of(pit, pend, [](
urls::param_view p)
{
if (!is_exact_topic{}(p))
return true;
boost::system::result<urls::url_view> u =
urls::parse_uri(p.value);
return u.has_value();
}))
return urls::grammar::error::invalid;
// all other fields are optional
// magnet link is OK
return m;
}
constexpr magnet_link_rule_t magnet_link_rule{};
/** Return a parsed magnet link from a string, or error.
This is a more convenient user-facing function
to parse magnet links.
*/
boost::system::result< magnet_link_view >
parse_magnet_link( core::string_view s ) noexcept
{
return urls::grammar::parse(s, magnet_link_rule);
}
int main(int argc, char** argv)
{
// This example shows how to use custom parsing
// to process alternate URI schemes, in this
// case "magnet"
if (argc != 2) {
std::cout << argv[0] << "\n";
std::cout << "magnet <link>\n"
"example: magnet magnet:?xt=urn:btih:d2474e86c95b19b8bcfdb92bc12c9d44667cfa36"
"&dn=Leaves+of+Grass+by+Walt+Whitman.epub"
"&tr=udp%3A%2F%2Ftracker.example4.com%3A80"
"&tr=udp%3A%2F%2Ftracker.example5.com%3A80"
"&tr=udp%3A%2F%2Ftracker.example3.com%3A6969"
"&tr=udp%3A%2F%2Ftracker.example2.com%3A80"
"&tr=udp%3A%2F%2Ftracker.example1.com%3A1337\n";
return EXIT_FAILURE;
}
boost::system::result<magnet_link_view> r =
parse_magnet_link(argv[1]);
if (!r)
return EXIT_FAILURE;
magnet_link_view m = *r;
std::cout << "link: " << m << "\n";
auto xt = m.exact_topics();
for (auto h : xt)
std::cout << "topic: " << h << "\n";
auto hs = m.info_hashes();
for (auto h : hs)
std::cout << "hash: " << h << "\n";
auto ps = m.protocols();
for (auto p : ps)
std::cout << "protocol: " << p << "\n";
auto tr = m.address_trackers();
for (auto h : tr)
std::cout << "tracker: " << h << "\n";
auto xs = m.exact_sources();
for (auto x : xs)
std::cout << "exact source: " << x << "\n";
auto as = m.acceptable_sources();
for (auto a : as)
std::cout << "topic: " << a << "\n";
auto mt = m.manifest_topics();
for (auto a : mt)
std::cout << "manifest topic: " << a << "\n";
auto ws = m.web_seed();
for (auto a : ws)
std::cout << "web seed: " << a << "\n";
auto kt = m.keyword_topic();
if (kt)
std::cout << "keyword topic: " << *kt << "\n";
auto dn = m.display_name();
if (dn)
std::cout << "display name: " << *dn << "\n";
return EXIT_SUCCESS;
}
----

View File

@@ -0,0 +1,428 @@
//
// Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/url
//
= mailto URLs
`mailto` is a URL scheme for email addresses. `mailto` URL are used on websites
to allow users to send an email to a specific address directly from an HTML document.
This example parses a mailto URL into a new view type and prints its components to
standard output.
// example_mailto
[source,cpp]
----
/*
This example parses a mailto URL into a new
view type and prints its components to
standard output.
*/
#include <boost/url/grammar/ci_string.hpp>
#include <boost/url/grammar/parse.hpp>
#include <boost/url/optional.hpp>
#include <boost/url/rfc/absolute_uri_rule.hpp>
#include <boost/url/url.hpp>
#include <boost/url/url_view.hpp>
#include <algorithm>
#include <iostream>
#include "rfc.hpp"
namespace urls = boost::urls;
// fwd-declaration for mailto_view
struct mailto_rule_t;
/// A new url type for mailto URLs
/**
This class represents a URI with the mailto
scheme.
Unlike a urls::url_view, which only represents
the general syntax of urls, a mailto_view
represents a reference to fields that are
relevant to mailto URLs, while ignoring
elements of the general syntax
that are not relevant to the scheme.
This allows us to use the general syntax
parsers to create a representation that
is more appropriate for the specified scheme
syntax.
@par Specification
@li <a href="https://www.rfc-editor.org/rfc/rfc6068"
>The 'mailto' URI Scheme</a>
@li <a href="https://www.rfc-editor.org/errata/rfc6068"
>RFC Errata Report</a>
@par References
@li <a href="https://en.wikipedia.org/wiki/Mailto"
>mailto (Wikipedia)</a>
*/
class mailto_view
{
urls::url_view u_;
public:
/// Return the specified email address in the URL
/**
A mailto URL might contain multiple email
addresses separated by commas.
The first addresses are represented in
the path. Other addresses are in
any query parameter whose key is "to".
@param i Address index
@return The specified address
*/
std::string
address(std::size_t i = 0) const;
/// @copydoc address()
urls::pct_string_view
encoded_address(std::size_t i = 0) const noexcept;
/// Return number of email addresses in the URL
std::size_t
size() const noexcept;
/// Return the specified cc email address in the URL
/**
A mailto URL might contain multiple cc
email addresses separated by commas.
Addresses can be represented in any query
parameter whose key is "cc".
@param i Address index
@return The specified cc address
*/
std::string
cc(std::size_t i) const;
/// @copydoc cc()
urls::pct_string_view
encoded_cc(std::size_t i) const noexcept;
/// Return number of "cc" email addresses in the URL
std::size_t
size_cc() const noexcept;
/// Return email message subject
std::string
subject() const;
/// @copydoc subject()
urls::pct_string_view
encoded_subject() const noexcept;
/// Return email message body
std::string
body() const;
/// @copydoc body()
urls::pct_string_view
encoded_body() const noexcept;
friend
std::ostream&
operator<<(std::ostream& os, mailto_view m)
{
return os << m.u_;
}
private:
// Count number of addresses in a string
static
std::size_t
addr_in_str(boost::core::string_view s);
// Get the ith address from a string
static
boost::optional<urls::pct_string_view>
get_nth_address(boost::core::string_view to, std::size_t &i) noexcept;
// Get param value or empty otherwise
urls::pct_string_view
param_or_empty(urls::pct_string_view k) const noexcept;
friend mailto_rule_t;
};
/** Rule to match a mailto URL
*/
struct mailto_rule_t
{
/// Value type returned by the rule
using value_type = mailto_view;
/// Parse a sequence of characters into a mailto_view
boost::system::result< value_type >
parse( char const*& it, char const* end ) const noexcept;
};
constexpr mailto_rule_t mailto_rule{};
/** Return a parsed mailto URL from a string, or error.
This is a more convenient user-facing function
to parse mailto URLs.
*/
boost::system::result< mailto_view >
parse_mailto( boost::core::string_view s ) noexcept
{
return urls::grammar::parse(s, mailto_rule);
}
int main(int argc, char** argv)
{
// This example shows how to use custom parsing
// to process alternate URI schemes, in this
// case "mailto"
if (argc != 2) {
std::cout << argv[0] << "\n";
std::cout << "mailto <URL>\n"
"examples:\n"
// Single e-mail address
"mailto mailto:someone@example.com\n"
// Two e-mail addresses
"mailto mailto:someone@example.com,someoneelse@example.com\n"
// E-mail headers
"mailto mailto:someone@example.com?subject=Our%20meeting&cc=someone_else@example.com&body=Hi%21\n"
// E-mail headers only
"mailto mailto:?to=&subject=mailto%20example&body=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FMailto\n"
// All fields
"mailto mailto:someone@example.com,%73omeoneelse@me.com?to=thirdperson@example.com&subject=Our%20meeting&cc=someone_else@example.com,onemore@ex%61mple.com&body=Hi%21\n";
return EXIT_FAILURE;
}
boost::system::result<mailto_view> r =
parse_mailto(argv[1]);
if (!r)
return EXIT_FAILURE;
mailto_view m = *r;
std::cout << "link: " << m << "\n";
for (std::size_t i = 0; i < m.size(); ++i)
std::cout <<
"to[" << i << "]: " <<
m.address(i) << "\n";
for (std::size_t i = 0; i < m.size_cc(); ++i)
std::cout <<
"cc[" << i << "]: " <<
m.address(i) << "\n";
std::cout << "subject: " << m.subject() << "\n";
std::cout << "body: " << m.body() << "\n";
return EXIT_SUCCESS;
}
std::string
mailto_view::address(std::size_t i) const
{
return encoded_address(i).decode();
}
urls::pct_string_view
mailto_view::encoded_address(std::size_t i) const noexcept
{
// Look for ith email address in the path string
auto s = get_nth_address(u_.encoded_path(), i);
if (s)
return *s;
// Look for ith email address in one of the "to" headers
auto ps = u_.encoded_params();
auto it = ps.find("to", urls::ignore_case);
while (it != ps.end())
{
s = get_nth_address((*it++).value, i);
if (s)
return *s;
it = ps.find(it, "to", urls::ignore_case);
}
return {};
}
std::size_t
mailto_view::size() const noexcept
{
// Count addresses in path
std::size_t n = addr_in_str(u_.encoded_path());
// Count addresses in "to" headers
auto ps = u_.encoded_params();
auto it = ps.find("to", urls::ignore_case);
while (it != ps.end())
{
n += addr_in_str((*it++).value);
it = ps.find(it, "to", urls::ignore_case);
}
return n;
}
std::string
mailto_view::cc(std::size_t i) const
{
return encoded_cc(i).decode();
}
urls::pct_string_view
mailto_view::encoded_cc(std::size_t i) const noexcept
{
// Look for ith email address in one of the "to" headers
auto ps = u_.encoded_params();
auto it = ps.find("cc", urls::ignore_case);
while (it != ps.end())
{
auto s = get_nth_address((*it++).value, i);
if (s)
return *s;
it = ps.find(it, "cc", urls::ignore_case);
}
return {};
}
std::size_t
mailto_view::size_cc() const noexcept
{
// Count addresses in "to" headers
std::size_t n = 0;
auto ps = u_.encoded_params();
auto it = ps.find("cc", urls::ignore_case);
while (it != ps.end())
{
n += addr_in_str((*it++).value);
it = ps.find(it, "cc", urls::ignore_case);
}
return n;
}
std::string
mailto_view::subject() const
{
return encoded_subject().decode();
}
urls::pct_string_view
mailto_view::encoded_subject() const noexcept
{
return param_or_empty("subject");
}
std::string
mailto_view::mailto_view::body() const
{
return encoded_body().decode();
}
urls::pct_string_view
mailto_view::encoded_body() const noexcept
{
return param_or_empty("body");
}
std::size_t
mailto_view::addr_in_str(boost::core::string_view s)
{
std::size_t n = 0;
bool empty = true;
for (char c : s)
{
if (c == ',')
{
n += !empty;
empty = true;
}
else
{
empty = false;
}
}
n += !empty;
return n;
}
boost::optional<urls::pct_string_view>
mailto_view::get_nth_address(boost::core::string_view to, std::size_t &i) noexcept
{
auto p = to.find(',');
while (p != boost::core::string_view::npos)
{
if (i == 0)
return urls::pct_string_view(
to.substr(0, p));
--i;
to.remove_prefix(p + 1);
p = to.find(',');
}
if (!to.empty())
{
if (i == 0)
return urls::pct_string_view(
to.substr(0, p));
--i;
}
return boost::none;
}
urls::pct_string_view
mailto_view::param_or_empty(urls::pct_string_view k) const noexcept
{
auto ps = u_.encoded_params();
auto it = ps.find(k, urls::ignore_case);
if (it != ps.end())
return (*it).value;
return {};
}
auto
mailto_rule_t::parse( char const*& it, char const* end ) const noexcept
-> boost::system::result< value_type >
{
// Syntax-based rules
boost::system::result<urls::url_view> r =
urls::grammar::parse(it, end, urls::absolute_uri_rule);
if (!r)
return r.error();
// Scheme-based rules
mailto_view m;
m.u_ = *r;
auto valid_header = [](urls::param_pct_view p) {
return
urls::grammar::parse(p.key, hfname_rule) &&
urls::grammar::parse(p.value, hfvalue_rule) &&
p.has_value &&
(!urls::grammar::ci_is_equal(p.key, "to") ||
urls::grammar::parse(p.value, addr_spec_rule));
};
auto ps = m.u_.encoded_params();
if (m.u_.scheme() == "mailto" &&
!m.u_.has_authority() &&
urls::grammar::parse(m.u_.encoded_path(), to_rule) &&
std::all_of(ps.begin(), ps.end(), valid_header))
return m;
return urls::grammar::error::invalid;
}
----

View File

@@ -0,0 +1,105 @@
//
// Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/url
//
= QR Code
A QR code is a machine-readable two-dimensional barcode. They might contain data
for a identifier or a URL to a website.
This example shows how to construct and modify URLs to consume a third party API to
generate QR Codes.
// example_qrcode
[source,cpp]
----
/*
This example shows how to construct and modify
URLs to consume a third party API to
generate QR Codes.
https://developers.google.com/chart/infographics/docs/qr_codes
*/
#include <boost/url/url.hpp>
#include <boost/core/detail/string_view.hpp>
#include <boost/url/parse.hpp>
#include <iostream>
namespace urls = boost::urls;
namespace core = boost::core;
int main(int argc, char** argv)
{
if (argc < 2) {
std::cout << argv[0] << "\n";
std::cout << "Usage: qrcode <data> <width> <height> <output encoding> <error correction> <border>\n"
"options:\n"
" <data>: The data to encode (required)\n"
" <width>: Image width (default: 100)\n"
" <height>: Image height (default: width)\n"
" <output encoding>: UTF-8, Shift_JIS, ISO-8859-1 (default: utf8)\n"
" <error correction>: percentage of error correction (default: 7)\n"
" <margin>: border width (default: 4)\n"
"examples:\n"
"qrcode \"Hello world\"\n";
return EXIT_FAILURE;
}
urls::url u =
urls::parse_uri(
"https://chart.googleapis.com/chart?cht=qr").value();
auto ps = u.params();
// Data
ps.append({"chl", argv[1]});
// Size
std::size_t width = argc < 3 ? 100 : std::stoll(argv[2]);
std::size_t height = argc < 4 ? width : std::stoll(argv[3]);
ps.append({"chs", std::to_string(width) + "x" + std::to_string(height)});
// Encoding
if (argc >= 5)
{
core::string_view output_encoding =
core::string_view(argv[3]) == "Shift_JIS" ||
core::string_view(argv[3]) == "ISO-8859-1" ?
argv[4] : "UTF-8";
ps.append({"choe", output_encoding});
}
// Error
if (argc >= 6)
{
std::size_t err = std::stoll(argv[5]);
std::string chld;
if (err < 11)
chld = "L";
else if (err < 20)
chld = "M";
else if (err < 27)
chld = "Q";
else
chld = "H";
std::size_t margin = argc < 7 ? 4 : std::stoll(argv[6]);
chld += "|";
chld += std::to_string(margin);
ps.append({"chld", chld});
}
std::cout << u << '\n';
return EXIT_SUCCESS;
}
----

View File

@@ -0,0 +1,333 @@
//
// Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/url
//
= Router
This example defines a router for URL paths. If the specified route matches one of the existing
routes, the example executes the underlying callback function.
// example_router
[source,cpp]
----
/*
This example defines a router for URL paths.
Each path is associated with a callback
function.
*/
#ifndef BOOST_URL_SOURCE
#define BOOST_URL_SOURCE
#endif
#include "router.hpp"
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/config.hpp>
#include <iostream>
#include <functional>
namespace urls = boost::urls;
namespace core = boost::core;
namespace asio = boost::asio;
namespace beast = boost::beast;
namespace http = beast::http;
using string_view = core::string_view;
using request_t = http::request<http::string_body>;
struct connection;
using handler = std::function<void(connection&, urls::matches)>;
int
serve(
urls::router<handler> const& r,
asio::ip::address const& a,
unsigned short port,
std::string const& doc_root);
struct connection
{
connection(asio::io_context& ioc)
: socket(ioc) {}
void
string_reply(core::string_view msg);
void
file_reply(core::string_view path);
void
error_reply(http::status, core::string_view msg);
beast::error_code ec;
asio::ip::tcp::socket socket;
std::string doc_root;
request_t req;
};
int
main(int argc, char **argv)
{
/*
* Parse cmd-line params
*/
if (argc != 4)
{
core::string_view exec = argv[0];
auto file_pos = exec.find_last_of("/\\");
if (file_pos != core::string_view::npos)
exec = exec.substr(file_pos + 1);
std::cerr
<< "Usage: " << exec
<< " <address> <port> <doc_root>\n"
"Example: " << exec << " 0.0.0.0 8080 .\n"
"Default values:\n"
"- address: 0.0.0.0\n"
"- port: 8080\n"
"- doc_root: ./\n";
}
auto const address = asio::ip::make_address(argc > 1 ? argv[1] : "0.0.0.0");
auto const port = static_cast<unsigned short>(argc > 2 ? std::atoi(argv[2]) : 8080);
auto const doc_root = std::string(argc > 3 ? argv[3] : ".");
/*
* Create router
*/
urls::router<handler> r;
r.insert("/", [&](connection& c, urls::matches const&) {
c.string_reply("Hello!");
});
r.insert("/user/{name}", [&](connection& c, urls::matches const& m) {
std::string msg = "Hello, ";
urls::pct_string_view(m[0]).decode({}, urls::string_token::append_to(msg));
msg += "!";
c.string_reply(msg);
});
r.insert("/user", [&](connection& c, urls::matches const&) {
std::string msg = "Users: ";
auto names = {"johndoe", "maria", "alice"};
for (auto name: names) {
msg += "<a href=\"/user/";
msg += name;
msg += "\">";
msg += name;
msg += "</a> ";
}
c.string_reply(msg);
});
r.insert("/public/{path+}", [&](connection& c, urls::matches m) {
c.file_reply(m["path"]);
});
return serve(r, address, port, doc_root);
}
#define ROUTER_CHECK(cond) if(!(cond)) { break; }
#define ROUTER_CHECK_EC(ec, cat) if(ec.failed()) { std::cerr << #cat << ": " << ec.message() << "\n"; break; }
int
serve(
urls::router<handler> const& r,
asio::ip::address const& address,
unsigned short port,
std::string const& doc_root)
{
/*
* Serve the routes with a simple synchronous
* server. This is an implementation detail
* in the context of this example.
*/
std::cout << "Listening on http://" << address << ":" << port << "\n";
asio::io_context ioc(1);
asio::ip::tcp::acceptor acceptor(ioc, {address, port});
urls::matches m;
for(;;)
{
connection c(ioc);
c.doc_root = doc_root;
acceptor.accept(c.socket);
beast::flat_buffer buffer;
for(;;)
{
// Read a request
http::read(c.socket, buffer, c.req, c.ec);
ROUTER_CHECK(c.ec != http::error::end_of_stream)
ROUTER_CHECK_EC(c.ec, read)
// Handle request
auto rpath = urls::parse_path(c.req.target());
if (c.req.method() != http::verb::get &&
c.req.method() != http::verb::head)
c.error_reply(
http::status::bad_request,
std::string("Unknown HTTP-method: ") +
std::string(c.req.method_string()));
else if (!rpath)
c.error_reply(http::status::bad_request, "Illegal request-target");
else if (auto h = r.find(*rpath, m))
(*h)(c, m);
else
c.error_reply(
http::status::not_found,
"The resource '" +
std::string(rpath->buffer()) +
"' was not found.");
ROUTER_CHECK_EC(c.ec, write)
ROUTER_CHECK(c.req.keep_alive())
}
c.socket.shutdown(asio::ip::tcp::socket::shutdown_send, c.ec);
}
return EXIT_SUCCESS;
}
#undef ROUTER_CHECK_EC
#undef ROUTER_CHECK
void
connection::
error_reply(http::status s, core::string_view msg)
{
// invalid route
http::response<http::string_body> res{s, req.version()};
res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
res.set(http::field::content_type, "text/html");
res.keep_alive(req.keep_alive());
res.body() = msg;
res.prepare_payload();
http::write(socket, res, ec);
}
void
connection::
string_reply(core::string_view msg)
{
http::response<http::string_body> res{http::status::ok, req.version()};
res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
res.set(http::field::content_type, "text/html");
res.keep_alive(req.keep_alive());
res.body() = msg;
res.prepare_payload();
http::write(socket, res, ec);
}
core::string_view
mime_type(core::string_view path);
std::string
path_cat(
beast::string_view base,
beast::string_view path);
void
connection::
file_reply(core::string_view path)
{
http::file_body::value_type body;
std::string jpath = path_cat(doc_root, path);
body.open(jpath.c_str(), beast::file_mode::scan, ec);
if(ec == beast::errc::no_such_file_or_directory)
{
error_reply(
http::status::not_found,
"The resource '" + std::string(path) +
"' was not found in " + jpath);
return;
}
auto const size = body.size();
http::response<http::file_body> res{
std::piecewise_construct,
std::make_tuple(std::move(body)),
std::make_tuple(http::status::ok, req.version())};
res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
res.set(http::field::content_type, mime_type(path));
res.content_length(size);
res.keep_alive(req.keep_alive());
http::write(socket, res, ec);
}
// Append an HTTP rel-path to a local filesystem path.
// The returned path is normalized for the platform.
std::string
path_cat(
core::string_view base,
core::string_view path)
{
if (base.empty())
return std::string(path);
std::string result(base);
#ifdef BOOST_MSVC
char constexpr path_separator = '\\';
#else
char constexpr path_separator = '/';
#endif
if( result.back() == path_separator &&
path.starts_with(path_separator))
result.resize(result.size() - 1);
else if (result.back() != path_separator &&
!path.starts_with(path_separator))
{
result.push_back(path_separator);
}
result.append(path.data(), path.size());
#ifdef BOOST_MSVC
for(auto& c : result)
if(c == '/')
c = path_separator;
#endif
return result;
}
core::string_view
mime_type(core::string_view path)
{
using beast::iequals;
auto const ext = [&path]
{
auto const pos = path.rfind(".");
if(pos == beast::string_view::npos)
return beast::string_view{};
return path.substr(pos);
}();
if(iequals(ext, ".htm")) return "text/html";
if(iequals(ext, ".html")) return "text/html";
if(iequals(ext, ".php")) return "text/html";
if(iequals(ext, ".css")) return "text/css";
if(iequals(ext, ".txt")) return "text/plain";
if(iequals(ext, ".js")) return "application/javascript";
if(iequals(ext, ".json")) return "application/json";
if(iequals(ext, ".xml")) return "application/xml";
if(iequals(ext, ".swf")) return "application/x-shockwave-flash";
if(iequals(ext, ".flv")) return "video/x-flv";
if(iequals(ext, ".png")) return "image/png";
if(iequals(ext, ".jpe")) return "image/jpeg";
if(iequals(ext, ".jpeg")) return "image/jpeg";
if(iequals(ext, ".jpg")) return "image/jpeg";
if(iequals(ext, ".gif")) return "image/gif";
if(iequals(ext, ".bmp")) return "image/bmp";
if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon";
if(iequals(ext, ".tiff")) return "image/tiff";
if(iequals(ext, ".tif")) return "image/tiff";
if(iequals(ext, ".svg")) return "image/svg+xml";
if(iequals(ext, ".svgz")) return "image/svg+xml";
return "application/text";
}
----

View File

@@ -0,0 +1,471 @@
//
// Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/url
//
= Sanitizing URLs
This example parses a non-strict or invalid URL
into path components according to its delimiters.
This pattern can be adapted to the requirements of other
applications.
Once the non-strict components are determined, a new URL is
created and its parts are set with the `set_encoded_X`
functions, which will encode any invalid chars accordingly.
This sort of transformation is useful in applications that are
extremely loose in what kinds of URLs they accept, such as
browsers. The sanitized URL can later be used for machine-to-machine
communication.
Using non-strict URLs directly is a security concern in
machine-to-machine communication, is ambiguous, and also
involve an extra cost for the transformations.
Different transformations are required by different applications to
construct a valid URL appropriate for machine-to-machine communication.
For instance, if an invalid relative reference includes something that
looks like a host in the first path segment, browsers usually interpret
that as the host with an implicit "https" scheme. Other applications
also have other implicit schemes.
The example also identifies whether the input url is already valid.
It includes diagnostics that can be used to help the user determine
if a URL is invalid and why it's invalid.
Once all transformations are applied, the result is a URL
appropriate for machine-to-machine communication.
// example_sanitize_url
[source,cpp]
----
/*
This example parses a non-strict / invalid URL
into path components according to its delimiters.
This pattern can be adapted to the requirements of other
applications.
Once the non-strict components are determined, a new URL is
created and its parts are set with the set_encoded_X
functions, which will encode any invalid chars accordingly.
This sort of transformation is useful in applications that are
extremely loose in what kinds of URLs they accept, such as
browsers. The sanitized URL can later be used for machine-to-machine
communication.
Using non-strict URLs directly is a security concern in
machine-to-machine communication, is ambiguous, and also
involve an extra cost for the transformations.
Different transformations are required by different applications to
construct a valid URL appropriate for machine-to-machine communication.
For instance, if an invalid relative reference includes something that
looks like a host in the first path segment, browsers usually interpret
that as the host with an implicit "https" scheme. Other applications
also have other implicit schemes.
The example also identifies whether the input url is already valid.
It includes diagnostics that can be used to help the user determine
if a URL is invalid and why it's invalid.
Once all transformations are applied, the result is a URL
appropriate for machine-to-machine communication.
*/
#include <boost/url/url.hpp>
#include <boost/url/parse.hpp>
#include <boost/url/parse_path.hpp>
#include <boost/url/string_view.hpp>
#include <boost/url/grammar/alpha_chars.hpp>
#include <boost/url/grammar/charset.hpp>
#include <boost/url/grammar/digit_chars.hpp>
#include <boost/url/grammar/lut_chars.hpp>
#include <algorithm>
#include <iostream>
#include <array>
namespace urls = boost::urls;
namespace core = boost::core;
struct url_components
{
core::string_view scheme;
core::string_view user;
core::string_view password;
core::string_view hostname;
core::string_view port;
core::string_view path;
core::string_view query;
core::string_view fragment;
};
core::string_view
port_of_scheme(core::string_view scheme_str) {
static std::array<std::pair<core::string_view, core::string_view>, 21> scheme_ports =
{{
{"http", "80"},
{"ftp", "21"},
{"https", "443"},
{"gopher", "70"},
{"ldap", "389"},
{"nntp", "119"},
{"snews", "563"},
{"imap", "143"},
{"pop", "110"},
{"sip", "5060"},
{"rtsp", "554"},
{"wais", "210"},
{"z39.50r", "210"},
{"z39.50s", "210"},
{"prospero", "191"},
{"nfs", "2049"},
{"tip", "3372"},
{"acap", "674"},
{"telnet", "23"},
{"ssh", "22"},
{"", "65535"}
}};
auto iequals = [](core::string_view a, core::string_view b)
{
if (b.size() != a.size()) {
return false;
}
for (unsigned int i = 0; i < a.size(); ++i) {
if (std::tolower(a[i]) != std::tolower(b[i])) {
return false;
}
}
return true;
};
auto const& it = std::find_if(
scheme_ports.begin(),
scheme_ports.end(),
[&](std::pair<core::string_view, core::string_view> const& s) {
return iequals(s.first, scheme_str);
});
if (it != scheme_ports.end()) {
return it->second;
} else {
return {};
}
}
void
extract_relative_ref(
core::string_view hostinfo_relative,
url_components &out)
{
// split path and query#fragment
constexpr urls::grammar::lut_chars path_end_chars("?#\0");
auto it = urls::grammar::find_if(
hostinfo_relative.begin(),
hostinfo_relative.end(), path_end_chars);
core::string_view query_and_frag = hostinfo_relative.substr(it - hostinfo_relative.begin());
if (query_and_frag != hostinfo_relative)
out.path = hostinfo_relative.substr(
0, query_and_frag.data() - hostinfo_relative.data());
if (query_and_frag.empty())
return;
// ?query#fragment
if (query_and_frag.front() == '?') {
query_and_frag = query_and_frag.substr(1);
core::string_view::size_type hash_pos = query_and_frag.find('#');
if (hash_pos != core::string_view::npos) {
core::string_view fragment_part = query_and_frag.substr(hash_pos);
out.fragment = fragment_part.substr(1);
out.query = query_and_frag.substr(
0, fragment_part.data() - query_and_frag.data());
} else {
out.query = query_and_frag;
}
return;
}
// fragment
out.fragment = query_and_frag.substr(1);
}
void
extract_userinfo_relative(
core::string_view relative_ref,
core::string_view userinfo_relative,
core::string_view host_info,
url_components& out) {
// We expect userinfo_relative to point to the first character of
// the hostname. If there's a port it is the first colon,
// except with IPv6.
auto host_end_pos = host_info.find(':');
if (host_end_pos == core::string_view::npos)
{
// definitely no port
out.hostname = userinfo_relative.substr(
0, relative_ref.data() - userinfo_relative.data());
return extract_relative_ref(relative_ref, out);
}
// extract hostname and port
out.hostname = userinfo_relative.substr(0, host_end_pos);
core::string_view host_relative = userinfo_relative.substr(host_end_pos + 1);
out.port = host_relative.substr(0, relative_ref.data() - host_relative.data());
// validate port
bool const valid_port =
urls::grammar::find_if_not(
out.port.begin(),
out.port.end(),
urls::grammar::digit_chars)
== out.port.end();
if (!valid_port)
{
// move port to hostname where it can be encoded
out.hostname = {out.hostname.begin(), out.port.end()};
out.port = {};
}
extract_relative_ref(relative_ref, out);
if (out.port.empty() && !out.scheme.empty())
out.port = port_of_scheme(out.scheme);
}
void
extract_scheme_relative(
core::string_view scheme_relative,
url_components &out)
{
// hostinfo
constexpr urls::grammar::lut_chars hostinfo_end_chars("/?#\0");
auto it = urls::grammar::find_if(
scheme_relative.begin(),
scheme_relative.end(),
hostinfo_end_chars);
auto path_offset = (std::min)(
scheme_relative.size(),
static_cast<std::size_t>(it - scheme_relative.begin()));
core::string_view host_info = scheme_relative.substr(0, path_offset);
// userinfo
core::string_view relative_ref = scheme_relative.substr(path_offset);
auto host_offset = host_info.find_last_of('@');
if (host_offset == core::string_view::npos)
return extract_userinfo_relative(
relative_ref,
scheme_relative,
host_info,
out);
// password
core::string_view userinfo_at_relative = scheme_relative.substr(host_offset);
core::string_view userinfo(host_info.data(), userinfo_at_relative.data() - host_info.data());
auto password_offset = std::min(userinfo.size(), userinfo.find(':'));
if (password_offset != userinfo.size()) {
out.user = scheme_relative.substr(0, password_offset);
core::string_view password = scheme_relative.substr(password_offset + 1);
out.password = password.substr(0, userinfo_at_relative.data() - password.data());
} else {
out.user = scheme_relative.substr(0, userinfo_at_relative.data() - scheme_relative.data());
}
// userinfo-relative
core::string_view userinfo_relative = userinfo_at_relative.substr(1);
it = urls::grammar::find_if(
userinfo_relative.begin(),
userinfo_relative.end(),
hostinfo_end_chars);
path_offset = (std::min)(
userinfo_relative.size(),
static_cast<std::size_t>(it - userinfo_relative.begin()));
host_info = userinfo_relative.substr(0, path_offset);
extract_userinfo_relative(
relative_ref,
userinfo_relative,
host_info,
out);
}
void
extract_uri_components(
core::string_view s,
url_components &out)
{
if (s.starts_with("//") && !s.starts_with("///"))
return extract_scheme_relative(s.substr(2), out);
if (s.starts_with('/'))
return extract_relative_ref(s, out);
// extract scheme
// first char in a scheme must be letter (we accept uppercase here)
bool has_scheme = false;
if (!s.empty() && urls::grammar::alpha_chars(s.front())) {
constexpr
urls::grammar::lut_chars scheme_chars(
"0123456789+-.ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
char const* it = urls::grammar::find_if_not(
s.begin() + 1, s.end(), scheme_chars);
size_t scheme_size = (std::min)(
s.size(), static_cast<std::size_t>(it - s.begin()));
// scheme must be non-empty and followed by ':'
if (s.size() > scheme_size && s[scheme_size] == ':') {
out.scheme = s.substr(0, scheme_size);
has_scheme = true;
}
}
// The usual route, parse scheme first
core::string_view scheme_relative = s;
if (has_scheme)
scheme_relative = s.substr(out.scheme.size() + 1);
const bool has_authority = scheme_relative.starts_with("//");
const bool is_relative_ref = !has_scheme && !has_authority;
if (is_relative_ref)
{
// this is the trick browsers usually apply when 1) there's no
// authority because the "//" is missing, 2) the scheme is also missing,
// and 3) the first path segment looks like an authority
//
// This behavior is widespread, although it's ambiguous because valid
// host characters are also valid path characters.
//
// It's this rule that allows for things like "www.boost.org" in the
// browser. This is an invalid URL because it has no "//" to indicate
// this is the authority and "www.boost.org" is a perfectly valid
// path segment.
auto first_seg_offset = (std::min)(s.size(), s.find_first_of('/'));
core::string_view first_seg = s.substr(0, first_seg_offset);
auto host_delimiter_pos = first_seg.find_first_of(".:");
bool const looks_like_authority =
urls::parse_authority(first_seg) &&
host_delimiter_pos != core::string_view::npos &&
host_delimiter_pos != first_seg.size() - 1;
if (looks_like_authority)
return extract_scheme_relative(s, out);
// if the first_seg is really a seg, parse as relative ref
return extract_relative_ref(s, out);
}
if (has_authority)
scheme_relative = scheme_relative.substr(2);
// all that's left is a relative path
return extract_relative_ref(scheme_relative, out);
}
void
sanitize_uri(core::string_view s, urls::url_base& dest) {
url_components o;
dest.clear();
extract_uri_components(s, o);
if (o.scheme.data())
dest.set_scheme(o.scheme);
if (o.user.data())
dest.set_encoded_user(o.user);
if (o.password.data())
dest.set_encoded_password(o.password);
if (o.hostname.data())
dest.set_encoded_host(o.hostname);
if (o.port.data())
dest.set_port(o.port);
if (o.path.data())
dest.set_encoded_path(o.path);
if (o.query.data())
dest.set_encoded_query(o.query);
if (o.fragment.data())
dest.set_encoded_fragment(o.fragment);
}
urls::url
sanitize_uri(core::string_view s) {
urls::url u;
sanitize_uri(s, u);
return u;
}
void
print_url_components(urls::url_view u)
{
std::cout << "url: " << u.buffer() << '\n';
if (u.has_scheme())
std::cout << "scheme: " << u.scheme() << '\n';
if (u.has_userinfo())
std::cout << "user: " << u.encoded_user() << '\n';
if (u.has_password())
std::cout << "password: " << u.encoded_password() << '\n';
if (u.has_authority())
std::cout << "hostname: " << u.encoded_host() << '\n';
if (u.has_port())
std::cout << "port: " << u.port() << '\n';
std::cout << "path: " << u.encoded_path() << '\n';
std::cout << "segments:\n";
for (auto seg: u.encoded_segments())
std::cout << "- " << seg << '\n';
if (u.has_query())
std::cout << "query: " << u.encoded_query() << '\n';
std::cout << "params:\n";
for (auto param: u.encoded_params())
{
if (param.has_value)
std::cout << "- " << param.key << ": " << param.value << '\n';
else
std::cout << "- " << param.key << '\n';
}
if (u.has_fragment())
std::cout << "fragment: " << u.encoded_fragment() << '\n';
}
int
main(int argc, char **argv)
{
if (argc != 2)
{
core::string_view exec = argv[0];
auto p = exec.find_last_of("/\\");
if (p != core::string_view::npos)
exec = exec.substr(p);
std::cerr
<< "Usage: " << exec
<< " <url>\n"
"target: a non-strict url\n";
return EXIT_FAILURE;
}
core::string_view uri_str = argv[1];
boost::system::result<urls::url_view> ru = urls::parse_uri_reference(uri_str);
if (ru)
{
urls::url_view u = *ru;
if (u.has_scheme() && u.has_fragment())
std::cout << "Input is a valid URL\n";
else if (u.has_scheme())
std::cout << "Input is a valid absolute URL\n";
else
std::cout << "Input is a valid relative URL\n";
print_url_components(u);
return EXIT_SUCCESS;
}
std::cout << "Sanitizing URL:\n";
std::cout << "input: " << uri_str << '\n';
urls::url u = sanitize_uri(uri_str);
print_url_components(u);
return EXIT_SUCCESS;
}
----

View File

@@ -8,9 +8,9 @@
//
== Character Sets
= Character Sets
A __character__ represents a subset of low-ASCII characters,
A __character set__ represents a subset of low-ASCII characters,
used as a building block for constructing rules. The library
models them as callable predicates invocable with this
equivalent signature:
@@ -22,14 +22,20 @@ bool( char ch ) const noexcept;
----
The `CharSet` concept describes the requirements on
The __CharSet__ concept describes the requirements on
syntax and semantics for these types. Here we declare
a character set type that includes the horizontal and
vertical whitespace characters:
[source,cpp]
----
// code_grammar_2_2
struct ws_chars_t
{
constexpr bool operator()( char c ) const noexcept
{
return c == '\t' || c == ' ' || c == '\r' || c == '\n';
}
};
----
@@ -38,7 +44,7 @@ the requirements:
[source,cpp]
----
// code_grammar_2_3
static_assert( is_charset< ws_chars_t >::value, "CharSet requirements not met" );
----
@@ -49,7 +55,7 @@ abstraction:
[source,cpp]
----
// code_grammar_2_4
constexpr ws_chars_t ws_chars{};
----
@@ -65,50 +71,73 @@ character:
[source,cpp]
----
// code_grammar_2_5
core::string_view get_token( core::string_view s ) noexcept
{
auto it0 = s.data();
auto const end = it0 + s.size();
// find the first non-whitespace character
it0 = find_if_not( it0, end, ws_chars );
if( it0 == end )
{
// all whitespace or empty string
return {};
}
// find the next whitespace character
auto it1 = find_if( it0, end, ws_chars );
// [it0, it1) is the part we want
return core::string_view( it0, it1 - it0 );
}
----
The function can now be called thusly:
// code_grammar_2_6
[source,cpp]
----
// code_grammar_2_6
assert( get_token( " \t john-doe\r\n \t jane-doe\r\n") == "john-doe" );
----
The library provides these often-used character sets:
// [table Character Sets [
// [Value]
// [Description]
// ][
// [[link url.ref.boost__urls__grammar__alnum_chars `alnum_chars`]]
// [
// Contains the uppercase and lowercase letters, and digits.
// ]
// ][
// [[link url.ref.boost__urls__grammar__alpha_chars `alpha_chars`]]
// [
// Contains the uppercase and lowercase letters.
// ]
// ][
// [[link url.ref.boost__urls__grammar__digit_chars `digit_chars`]]
// [
// Contains the decimal digit characters.
// ]
// ][
// [[link url.ref.boost__urls__grammar__hexdig_chars `hexdig_chars`]]
// [
// Contains the uppercase and lowercase hexadecimal
// digit characters.
// ]
// ][
// [[link url.ref.boost__urls__grammar__vchars `vchars`]]
// [
// Contains the visible characters (i.e. non whitespace).
// ]
// ]]
[cols="a,a"]
|===
// Headers
|Value|Description
// Row 1, Column 1
|`alnum_chars`
// Row 1, Column 2
|Contains the uppercase and lowercase letters, and digits.
// Row 2, Column 1
|`alpha_chars`
// Row 2, Column 2
|Contains the uppercase and lowercase letters.
// Row 3, Column 1
|`digit_chars`
// Row 3, Column 2
|Contains the decimal digit characters.
// Row 4, Column 1
|`hexdig_chars`
// Row 4, Column 2
|Contains the uppercase and lowercase hexadecimal
digit characters.
// Row 5, Column 1
|`vchars`
// Row 5, Column 2
|Contains the visible characters (i.e. non whitespace).
|===
Some of the character sets in the library have implementations
@@ -119,18 +148,19 @@ using Streaming SIMD Extensions 2
(https://en.wikipedia.org/wiki/SSE2[SSE2,window=blank_]),
available on all x86 and x64 architectures.
=== The lut_chars Type
== The lut_chars Type
The `lut_chars` type satisfies the `CharSet`
The `lut_chars` type satisfies the __CharSet__
requirements and offers an optimized `constexpr`
implementation which provides enhanced performance
and notational convenience for specifying character
sets. Compile-time instances can be constructed
from strings:
// code_grammar_2_7
[source,cpp]
----
// code_grammar_2_7
constexpr lut_chars vowels = "AEIOU" "aeiou";
----
@@ -138,9 +168,10 @@ We can use `operator+` and `operator-` notation to add and
remove elements from the set at compile time. For example,
sometimes the character 'y' sounds like a vowel:
// code_grammar_2_8
[source,cpp]
----
// code_grammar_2_8
constexpr auto vowels_and_y = vowels + 'y' + 'Y';
----
@@ -151,7 +182,14 @@ Here we create the set of visible characters using a lambda:
[source,cpp]
----
// code_grammar_2_9
struct is_visible
{
constexpr bool operator()( char ch ) const noexcept
{
return ch >= 33 && ch <= 126;
}
};
constexpr lut_chars visible_chars( is_visible{} ); // (since C++11)
----
@@ -159,23 +197,25 @@ Alternatively:
[source,cpp]
----
// code_grammar_2_10
constexpr lut_chars visible_chars( [](char ch) { return ch >= 33 && ch <= 126; } ); // (since C++17)
----
Differences can be calculated with `operator-`:
// code_grammar_2_11
[source,cpp]
----
// code_grammar_2_11
constexpr auto visible_non_vowels = visible_chars - vowels;
----
We can also remove individual characters:
// code_grammar_2_12
[source,cpp]
----
// code_grammar_2_12
constexpr auto visible_non_vowels_or_y = visible_chars - vowels - 'y';
----

View File

@@ -8,21 +8,21 @@
//
== Compound Rules
= Compound Rules
The rules shown so far have defined
https://en.wikipedia.org/wiki/Terminal_and_nonterminal_symbols[__terminal__,window=blank_],
https://en.wikipedia.org/wiki/Terminal_and_nonterminal_symbols[__terminal symbols__,window=blank_],
representing indivisible units of grammar. To parse more
complex things, a
https://en.wikipedia.org/wiki/Parser_combinator[__parser__,window=blank_]
(or __compound__) is a rule which accepts as parameters one
https://en.wikipedia.org/wiki/Parser_combinator[__parser combinator__,window=blank_]
(or __compound rule__) is a rule which accepts as parameters one
or more rules and combines them to form a higher order algorithm.
In this section we introduce the compound rules provided by the
library, and how they may be used to express more complex grammars.
=== Tuple Rule
== Tuple Rule
Consider the following grammar:
@@ -37,9 +37,10 @@ or more specified rules in sequence. The folllowing defines
a sequence using some character literals and two decimal octets,
which is a fancy way of saying a number between 0 and 255:
// code_grammar_3_1
[source,cpp]
----
// code_grammar_3_1
constexpr auto version_rule = tuple_rule( delim_rule( 'v' ), dec_octet_rule, delim_rule( '.' ), dec_octet_rule );
----
@@ -48,22 +49,26 @@ to the value type of each rule specified upon construction. The decimal
octets are represented by the `dec_octet_rule` which stores its
result in an `unsigned char`:
// code_grammar_3_2
[source,cpp]
----
// code_grammar_3_2
system::result< std::tuple< core::string_view, unsigned char, core::string_view, unsigned char > > rv = parse( "v42.44800", version_rule );
----
To extract elements from `std::tuple` the function `std::get`
To extract elements from `std::tuple` the function https://en.cppreference.com/w/cpp/utility/tuple/get[`std::get`,window=blank_]
must be used. In this case, we don't care to know the value for
the matching character literals. The `tuple_rule` discards match
results whose value type is `void`. We can use the `squelch`
compound rule to convert a matching value type to `void`, and
reformulate our rule:
// code_grammar_3_3
[source,cpp]
----
// code_grammar_3_3
constexpr auto version_rule = tuple_rule( squelch( delim_rule( 'v' ) ), dec_octet_rule, squelch( delim_rule( '.' ) ), dec_octet_rule );
system::result< std::tuple< unsigned char, unsigned char > > rv = parse( "v42.44800", version_rule );
----
@@ -71,33 +76,61 @@ When all but one of the value types is `void`, the `std::tuple` is
elided and the remaining value type is promoted to the result of
the match:
// code_grammar_3_4
[source,cpp]
----
// code_grammar_3_4
// port = ":" unsigned-short
constexpr auto port_rule = tuple_rule( squelch( delim_rule( ':' ) ), unsigned_rule< unsigned short >{} );
system::result< unsigned short > rv = parse( ":443", port_rule );
----
=== Optional Rule
== Optional Rule
BNF elements in brackets denote optional components. These are
expressed using `optional_rule`, whose value type is an
`optional`. For example, we can adapt the port rule from
above to be an optional component:
// code_grammar_3_5
[source,cpp]
----
// code_grammar_3_5
// port = [ ":" unsigned-short ]
constexpr auto port_rule = optional_rule( tuple_rule( squelch( delim_rule( ':' ) ), unsigned_rule< unsigned short >{} ) );
system::result< boost::optional< unsigned short > > rv = parse( ":8080", port_rule );
assert( rv->has_value() && rv->value() == 8080 );
----
In this example we build up a rule to represent an
endpoint as an IPv4 address with an optional port:
// code_grammar_3_6
[source,cpp]
----
// code_grammar_3_6
// ipv4_address = dec-octet "." dec-octet "." dec-octet "." dec-octet
//
// port = ":" unsigned-short
//
// endpoint = ipv4_address [ port ]
constexpr auto endpoint_rule = tuple_rule(
tuple_rule(
dec_octet_rule, squelch( delim_rule( '.' ) ),
dec_octet_rule, squelch( delim_rule( '.' ) ),
dec_octet_rule, squelch( delim_rule( '.' ) ),
dec_octet_rule ),
optional_rule(
tuple_rule(
squelch( delim_rule( ':' ) ),
unsigned_rule< unsigned short >{} ) ) );
----
@@ -105,19 +138,27 @@ This can be simplified; the library provides `ipv4_address_rule`
whose result type is `ipv4_address`, offering more utility
than representing the address simply as a collection of four numbers:
// code_grammar_3_7
[source,cpp]
----
// code_grammar_3_7
constexpr auto endpoint_rule = tuple_rule(
ipv4_address_rule,
optional_rule(
tuple_rule(
squelch( delim_rule( ':' ) ),
unsigned_rule< unsigned short >{} ) ) );
system::result< std::tuple< ipv4_address, boost::optional< unsigned short > > > rv = parse( "192.168.0.1:443", endpoint_rule );
----
=== Variant Rule
== Variant Rule
BNF elements separated by unquoted slashes represent a set
of alternatives from which one element may match. We represent
them using `variant_rule`, whose value type is a `variant`.
them using `variant_rule`, whose value type is a variant.
Consider the following HTTP production rule which comes from
https://datatracker.ietf.org/doc/html/rfc7230#section-5.3"[rfc7230,window=blank_]:
@@ -135,9 +176,16 @@ define the rule, using `origin_form_rule`, `absolute_uri_rule`,
and `authority_rule` which come with the library, and obtain
a result from parsing a string:
// code_grammar_3_8
[source,cpp]
----
// code_grammar_3_8
constexpr auto request_target_rule = variant_rule(
origin_form_rule,
absolute_uri_rule,
authority_rule,
delim_rule('*') );
system::result< variant2::variant< url_view, url_view, authority_view, core::string_view > > rv = parse( "/results.htm?page=4", request_target_rule );
----

View File

@@ -8,7 +8,8 @@
//
// [section:grammar Customization]
[#grammar]
= Customization
For a wide range of applications the library's container interfaces
are sufficient for URLs using the generic syntax or the well known
@@ -17,7 +18,7 @@ to go beyond what the library offers:
* Create new custom containers for other schemes
* Incorporate the parsing of URLs in an enclosing grammar
* Parse `rfc3986` elements in non-URL contexts
* Parse https://tools.ietf.org/html/rfc3986[rfc3986,window=blank_] elements in non-URL contexts
(`authority_view` is an example of this).
* Define new ABNF rules used to parse non-URL strings
@@ -49,22 +50,26 @@ understanding of this notation is necessary to achieve
best results for learning how to use the custom parsing
features.
// [note
// Code samples and identifiers in this customization
// section are written as if the following declarations
// are in effect:
//
// ```
// #include <boost/url/grammar.hpp>
//
// using namespace ::boost::urls::grammar;
// ```
// ]
[NOTE]
====
Code samples and identifiers in this customization
section are written as if the following declarations
are in effect:
[source,cpp]
----
#include <boost/url/grammar.hpp>
using namespace ::boost::urls::grammar;
----
====
// [include 4.1.rules.qbk]
// [include 4.2.charset.qbk]
// [include 4.3.combinators.qbk]
// [include 4.4.range.qbk]
// [include 4.5.rfc3986.qbk]

View File

@@ -8,7 +8,7 @@
//
== Ranges
= Ranges
@@ -31,9 +31,11 @@ rule allowing for a prescribed number of repetitions of a specified
rule. The following rule matches the grammar for __chunk-ext__
defined above:
// code_grammar_4_1
[source,cpp]
----
// code_grammar_4_1
constexpr auto chunk_ext_rule = range_rule(
tuple_rule( squelch( delim_rule( ';' ) ), token_rule( alnum_chars ) ) );
----
@@ -43,9 +45,13 @@ case, the type is `string_view` because the tuple has one unsquelched
element, the `token_rule`. The range can be iterated to produce results,
without allocating memory for each element. The following code:
// code_grammar_4_2
[source,cpp]
----
// code_grammar_4_2
system::result< range< core::string_view > > rv = parse( ";johndoe;janedoe;end", chunk_ext_rule );
for( auto s : rv.value() )
std::cout << s << "\n";
----
@@ -77,17 +83,25 @@ the minimum number of repetitions, or both the minimum and maximum
number of repetitions. Since our list may not be empty, the following
rule perfectly captures the __token-list__ grammar:
// code_grammar_4_3
[source,cpp]
----
// code_grammar_4_3
constexpr auto token_list_rule = range_rule(
token_rule( alnum_chars ),
tuple_rule( squelch( delim_rule( ',' ) ), token_rule( alnum_chars ) ),
1 );
----
The following code:
// code_grammar_4_4
[source,cpp]
----
// code_grammar_4_4
system::result< range< core::string_view > > rv = parse( "johndoe,janedoe,end", token_list_rule );
for( auto s : rv.value() )
std::cout << s << "\n";
----
@@ -102,68 +116,71 @@ end
In the next section we discuss the available rules
which are specific to `rfc3986`.
which are specific to https://tools.ietf.org/html/rfc3986[rfc3986,window=blank_].
=== More
== More
These are the rules and compound rules provided by the
library. For more details please see the corresponding
reference sections.
// [table Grammar Symbols [
// [Name]
// [Description]
// ][
// [__dec_octet_rule__]
// [
// Match an integer from 0 and 255.
// ]
// ][
// [__delim_rule__]
// [
// Match a character literal.
// ]
// ][
// [__literal_rule__]
// [
// Match a character string exactly.
// ]
// ][
// [__not_empty_rule__]
// [
// Make a matching empty string into an error instead.
// ]
// ][
// [__optional_rule__]
// [
// Ignore a rule if parsing fails, leaving
// the input pointer unchanged.
// ]
// ][
// [__range_rule__]
// [
// Match a repeating number of elements.
// ]
// ][
// [__token_rule__]
// [
// Match a string of characters from a character set.
// ]
// ][
// [__tuple_rule__]
// [
// Match a sequence of specified rules, in order.
// ]
// ][
// [__unsigned_rule__]
// [
// Match an unsigned integer in decimal form.
// ]
// ][
// [__variant_rule__]
// [
// Match one of a set of alternatives specified by rules.
// ]
// ]]
[cols="a,a"]
|===
// Headers
|Name|Description
// Row 1, Column 1
|`dec_octet_rule`
// Row 1, Column 2
|Match an integer from 0 and 255.
// Row 2, Column 1
|`delim_rule`
// Row 2, Column 2
|Match a character literal.
// Row 3, Column 1
|`literal_rule`
// Row 3, Column 2
|Match a character string exactly.
// Row 4, Column 1
|`not_empty_rule`
// Row 4, Column 2
|Make a matching empty string into an error instead.
// Row 5, Column 1
|`optional_rule`
// Row 5, Column 2
|Ignore a rule if parsing fails, leaving
the input pointer unchanged.
// Row 6, Column 1
|`range_rule`
// Row 6, Column 2
|Match a repeating number of elements.
// Row 7, Column 1
|`token_rule`
// Row 7, Column 2
|Match a string of characters from a character set.
// Row 8, Column 1
|`tuple_rule`
// Row 8, Column 2
|Match a sequence of specified rules, in order.
// Row 9, Column 1
|`unsigned_rule`
// Row 9, Column 2
|Match an unsigned integer in decimal form.
// Row 10, Column 1
|`variant_rule`
// Row 10, Column 2
|Match one of a set of alternatives specified by rules.
|===

View File

@@ -8,7 +8,7 @@
//
== RFC 3986
= RFC 3986
Functions like `parse_uri` are sufficient for converting URLs but
they require that the entire string is consumed. When URLs appear as
@@ -16,9 +16,9 @@ components of a larger grammar, it is desired to use composition
of rules based parsing to process these along with other elements
potentially unrelated to resource locators. To achieve this, the
library provides rules for the top-level BNF productions found
in `rfc3986` and a rule for matching percent-encoded strings.
in https://tools.ietf.org/html/rfc3986[rfc3986,window=blank_] and a rule for matching percent-encoded strings.
=== Percent Encoding
== Percent Encoding
The percent-encoding mechanism is used to represent a data octet
in a component when the corresponding character is outside the
@@ -58,56 +58,63 @@ equality and comparison to unencoded strings, without allocating
memory. In the example below we parse the string `s` as a
series of zero or more `pchars`:
// code_grammar_5_1
[source,cpp]
----
// code_grammar_5_1
system::result< pct_string_view > rv = parse( s, pct_encoded_rule( pchars ) );
----
These constants are used and provided by the library to
specify rules for percent-encoded URL components:
// [table URL Character Sets [
// [Name]
// [BNF]
// ][
// [[link url.ref.boost__urls__gen_delim_chars `gen_delim_chars`]]
// [
// ```
// gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
// ```
// ]
// ][
// [[link url.ref.boost__urls__pchars `pchars`]]
// [
// ```
// pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
// ```
// ]
// ][
// [[link url.ref.boost__urls__reserved_chars `reserved_chars`]]
// [
// (everything but
// [link url.ref.boost__urls__unreserved_chars `unreserved_chars`])
// ]
// ][
// [[link url.ref.boost__urls__sub_delim_chars `sub_delim_chars`]]
// [
// ```
// sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
// / "*" / "+" / "," / ";" / "="
// ```
// ]
// ][
// [[link url.ref.boost__urls__unreserved_chars `unreserved_chars`]]
// [
// ```
// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
// ```
// ]
// ]]
[cols="a,a"]
|===
// Headers
|Name|BNF
=== URL Rules
// Row 1, Column 1
|`gen_delim_chars`
// Row 1, Column 2
|[source]
----
gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
----
// Row 2, Column 1
|`pchars`
// Row 2, Column 2
|[source]
----
pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
----
// Row 3, Column 1
|`reserved_chars`
// Row 3, Column 2
|(everything but `unreserved_chars`)
// Row 4, Column 1
|`sub_delim_chars`
// Row 4, Column 2
|[source]
----
sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
/ "*" / "+" / "," / ";" / "="
----
// Row 5, Column 1
|`unreserved_chars`
// Row 5, Column 2
|[source]
----
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
----
|===
== URL Rules
When a URL can appear in the context of a larger grammar, it may
be desired to express the enclosing grammar in a single rule that
@@ -119,67 +126,90 @@ elements. Here we present a rule suitable for parsing the
the HTTP
https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.1[__request-line__,window=blank_]:
// code_grammar_5_2
[source,cpp]
----
// code_grammar_5_2
// request-line = method SP request-target SP HTTP-version CRLF
constexpr auto request_line_rule = tuple_rule(
not_empty_rule( token_rule( alpha_chars ) ), // method
squelch( delim_rule( ' ' ) ), // SP
variant_rule(
absolute_uri_rule, // absolute-uri or
relative_ref_rule), // relative-ref
squelch( delim_rule( ' ' ) ),
squelch( literal_rule( "HTTP/" ) ), // "HTTP/"
delim_rule( digit_chars ), // DIGIT
squelch( delim_rule( '.' ) ), // "."
delim_rule( digit_chars ), // DIGIT
squelch( literal_rule( "\r\n" ) ) ); // CRLF
----
The library offers these rules to allow custom rule definitions
to integrate the various styles of valid URL rules:
// [table RFC3986 Rules [
// [Name]
// [BNF]
// ][
// [[link url.ref.boost__urls__absolute_uri_rule `absolute_uri_rule`]]
// [
// ```
// absolute-URI = scheme ":" hier-part [ "?" query ]
//
// hier-part = "//" authority path-abempty
// / path-absolute
// / path-rootless
// / path-empty
// ```
// ]
// ][
// [[link url.ref.boost__urls__authority_rule `authority_rule`]]
// [
// ```
// authority = [ userinfo "@" ] host [ ":" port ]
// ```
// ]
// ][
// [[link url.ref.boost__urls__origin_form_rule `origin_form_rule`]]
// [
// ```
// origin-form = absolute-path [ "?" query ]
//
// absolute-path = 1*( "/" segment )
// ```
// ]
// ][
// [[link url.ref.boost__urls__relative_ref_rule `relative_ref_rule`]]
// [
// ```
// relative-ref = relative-part [ "?" query ] [ "#" fragment ]
// ```
// ]
// ][
// [[link url.ref.boost__urls__uri_reference_rule `uri_reference_rule`]]
// [
// ```
// URI-reference = URI / relative-ref
// ```
// ]
// ][
// [[link url.ref.boost__urls__uri_rule `uri_rule`]]
// [
// ```
// URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
// ```
// ]
// ]]
[cols="a,a"]
|===
// Headers
|Name|BNF
// Row 1, Column 1
|`absolute_uri_rule`
// Row 1, Column 2
|[source]
----
absolute-URI = scheme ":" hier-part [ "?" query ]
hier-part = "//" authority path-abempty
/ path-absolute
/ path-rootless
/ path-empty
----
// Row 2, Column 1
|`authority_rule`
// Row 2, Column 3
|[source,cpp]
----
authority = [ userinfo "@" ] host [ ":" port ]
----
// Row 3, Column 1
|`origin_form_rule`
// Row 3, Column 2
|[source]
----
origin-form = absolute-path [ "?" query ]
absolute-path = 1*( "/" segment )
----
// Row 4, Column 1
|`relative_ref_rule`
// Row 4, Column 2
|[source]
----
relative-ref = relative-part [ "?" query ] [ "#" fragment ]
----
// Row 5, Column 1
|`uri_reference_rule`
// Row 5, Column 2
|[source,cpp]
----
URI-reference = URI / relative-ref
----
// Row 6, Column 1
|`uri_rule`
// Row 6, Column 2
|[source,cpp]
----
URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
----
|===

View File

@@ -8,9 +8,9 @@
//
== Parse Rules
= Parse Rules
A `Rule` is an object which tries to match the beginning of
A __Rule__ is an object which tries to match the beginning of
an input character buffer against a particular syntax.
It returns a `result` containing a value if the match
was successful, or an `error_code` if the match failed.
@@ -24,19 +24,38 @@ parsed sequentially:
[source,cpp]
----
// code_grammar_1_1
template< class Rule >
auto parse( core::string_view s, Rule const& r) -> system::result< typename Rule::value_type >;
template< class Rule >
auto parse( char const *& it, char const* end, Rule const& r) -> system::result< typename Rule::value_type >;
----
To satisfy the `Rule` concept, a `class` or `struct` must declare
To satisfy the __Rule__ concept, a `class` or `struct` must declare
the nested type `value_type` indicating the type of value returned
upon success, and a `const` member function `parse` with a prescribed
signature. In the following code we define a rule that matches a
single comma:
// code_grammar_1_2
[source,cpp]
----
// code_grammar_1_2
struct comma_rule_t
{
// The type of value returned upon success
using value_type = core::string_view;
// The algorithm which checks for a match
system::result< value_type >
parse( char const*& it, char const* end ) const
{
if( it != end && *it == ',')
return core::string_view( it++, 1 );
return error::mismatch;
}
};
----
@@ -44,18 +63,22 @@ Since rules are passed by value, we declare a `constexpr` variable
of the type for syntactical convenience. Variable names for rules
are usually suffixed with `_rule`:
// code_grammar_1_3
[source,cpp]
----
// code_grammar_1_3
constexpr comma_rule_t comma_rule{};
----
Now we can call `parse` with the string of input and the rule
variable thusly:
// code_grammar_1_4
[source,cpp]
----
// code_grammar_1_4
system::result< core::string_view > rv = parse( ",", comma_rule );
assert( rv.has_value() && rv.value() == "," );
----
@@ -65,9 +88,10 @@ an unsigned decimal integer. Here we construct the rule at
run time and specify the type of unsigned integer used to
hold the result with a template parameter:
// code_grammar_1_5
[source,cpp]
----
// code_grammar_1_5
system::result< unsigned short > rv = parse( "16384", unsigned_rule< unsigned short >{} );
----
@@ -76,13 +100,14 @@ passed character literal. This is a more general version of the
comma rule which we defined earlier. There is also an overload
which matches exactly one character from a character set.
// code_grammar_1_6
[source,cpp]
----
// code_grammar_1_6
system::result< core::string_view > rv = parse( ",", delim_rule(',') );
----
=== Error Handling
== Error Handling
When a rule fails to match, or if the rule detects a
unrecoverable problem with the input, it returns a
@@ -97,7 +122,7 @@ in the input, or to the `end` pointer if all input
was consumed.
It is the responsibilty of library and user-defined
implementations of __compound__ (explained later)
implementations of __compound rules__ (explained later)
to rewind their internal pointer if a parsing operation
was unsuccessful, and they wish to attempt parsing the
same input using a different rule.

View File

@@ -7,16 +7,12 @@
// Official repository: https://github.com/boostorg/url
//
== Boost.URL
= Boost.URL
Boost.URL is a portable C++ library which provides containers and algorithms
which model a "URL," more formally described using the
https://datatracker.ietf.org/doc/html/rfc3986[Uniform Resource Identifier (URI),window=blank_]
specification (henceforth referred to as `rfc3986`). A URL is a compact sequence
specification (henceforth referred to as https://tools.ietf.org/html/rfc3986[rfc3986,window=blank_]). A URL is a compact sequence
of characters that identifies an abstract or physical resource. For example,
this is a valid URL:
@@ -31,7 +27,7 @@ This library understands the grammars related to URLs and provides
functionality to validate, parse, examine, and modify urls, and apply
normalization or resolution algorithms.
=== Features
== Features
While the library is general purpose, special care has been taken to ensure
that the implementation and data representation are friendly to network
@@ -47,23 +43,25 @@ Boost.URL offers these features:
* C++11 as only requirement
* Fast compilation, few templates
* Strict compliance with `rfc3986`
* Strict compliance with https://tools.ietf.org/html/rfc3986[rfc3986,window=blank_]
* Containers that maintain valid URLs
* Parsing algorithms that work without exceptions
* Control over storage and allocation for URLs
* Support for `-fno-exceptions`, detected automatically
* Features that work well on embedded devices
// [note
// Currently the library does not handle
// [@https://www.rfc-editor.org/rfc/rfc3987.html Internationalized Resource Identifiers] (IRIs).
// These are different from URLs, come from Unicode strings instead of
// low-ASCII strings, and are covered by a separate specification.
// ]
[NOTE]
====
Currently the library does not handle
https://www.rfc-editor.org/rfc/rfc3987.html[Internationalized Resource Identifiers,window=blank_] (IRIs).
These are different from URLs, come from Unicode strings instead of
low-ASCII strings, and are covered by a separate specification.
====
=== Requirements
== Requirements
The library requires a compiler supporting at least C++11.
@@ -79,7 +77,7 @@ desired.
=== Tested Compilers
== Tested Compilers
Boost.URL has been tested with the following compilers:
@@ -91,7 +89,7 @@ and these architectures: x86, x64, ARM64, S390x.
We do not test and support gcc 8.0.1.
=== Quality Assurance
== Quality Assurance
The development infrastructure for the library includes
these per-commit analyses:
@@ -102,7 +100,7 @@ these per-commit analyses:
=== Nomenclature
== Nomenclature
Various names have been used historically to refer to different
flavors of resource identifiers, including __URI__, __URL__, __URN__,
@@ -110,13 +108,13 @@ and even __IRI__. Over time, the distinction between URIs and URLs
has disappeared when discussed in technical documents and
informal works. In this library we use the term **URL** to
refer to all strings which are valid according to the
top-level grammar rules found in `rfc3986`.
top-level grammar rules found in https://tools.ietf.org/html/rfc3986[rfc3986,window=blank_].
=== ABNF
== ABNF
This documentation uses the Augmented
https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form[Backus-Naur Form,window=blank_]
@@ -131,7 +129,7 @@ the library.
=== Acknowledgments
== Acknowledgments
This library wouldn't be where it is today without the help of
https://github.com/pdimov[Peter Dimov,window=blank_]
@@ -139,6 +137,6 @@ for design advice and general assistance.
// [include 2.0.quicklook.qbk]

View File

@@ -8,19 +8,26 @@
//
== Quick Look
= Quick Look
This section is intended to give the reader a brief overview of the features
and interface style of the library.
=== Integration
== Integration
[NOTE]
====
Sample code and identifiers used throughout are written as if
the following declarations are in effect:
[source,cpp]
----
#include <boost/url.hpp>
using namespace boost::urls;
----
====
// [note
// Sample code and identifiers used throughout are written as if
// the following declarations are in effect:
//
// [snippet_headers_3]
// ]
We begin by including the library header file which brings all the symbols into
scope.
@@ -28,7 +35,7 @@ scope.
[source,cpp]
----
// snippet_headers_1
#include <boost/url.hpp>
----
@@ -41,14 +48,15 @@ You must install binaries in a location that can be found by your linker.
If you followed the http://www.boost.org/doc/libs/release/more/getting_started/index.html[Boost Getting Started,window=blank_]
instructions, that's already been done for you.
=== Parsing
== Parsing
Say you have the following URL that you want to parse:
// code_urls_parsing_1
[source,cpp]
----
// code_urls_parsing_1
boost::core::string_view s = "https://user:pass@example.com:443/path/to/my%2dfile.txt?id=42&name=John%20Doe+Jingleheimer%2DSchmidt#page%20anchor";
----
@@ -59,9 +67,10 @@ The library namespace includes the aliases `string_view`, `error_code`, and
You can parse the string by calling this function:
// code_urls_parsing_2
[source,cpp]
----
// code_urls_parsing_2
boost::system::result<url_view> r = parse_uri( s );
----
@@ -71,17 +80,19 @@ A number of functions are available to parse different types of URL.
We can immediately call `result::value` to obtain a `url_view`.
// snippet_parsing_3
[source,cpp]
----
// snippet_parsing_3
url_view u = r.value();
----
Or simply
// snippet_parsing_4
[source,cpp]
----
// snippet_parsing_4
url_view u = *r;
----
@@ -95,52 +106,85 @@ https://www.boost.org/doc/libs/1_83_0//libs/system/doc/html/system.html#ref_quer
https://www.boost.org/doc/libs/1_83_0//libs/system/doc/html/system.html#ref_queries[`result::has_error`,window=blank_] could
also be used to check if the string has been parsed without errors.
// [note
// It is worth noting that __parse_uri__ does not allocate any memory dynamically.
// Like a __string_view__, a __url_view__ does not retain ownership of the underlying
// string buffer.
//
// As long as the contents of the original string are unmodified, constructed
// URL views always contain a valid URL in its correctly serialized form.
//
// If the input does not match the URL grammar, an error code
// is reported through __result__ rather than exceptions.
// Exceptions only thrown on excessive input length.
// ]
[NOTE]
====
It is worth noting that `parse_uri` does not allocate any memory dynamically.
Like a `string_view`, a `url_view` does not retain ownership of the underlying
string buffer.
=== Accessing
As long as the contents of the original string are unmodified, constructed
URL views always contain a valid URL in its correctly serialized form.
If the input does not match the URL grammar, an error code
is reported through `result` rather than exceptions.
Exceptions only thrown on excessive input length.
====
== Accessing
Accessing the parts of the URL is easy:
// snippet_accessing_1
[source,cpp]
----
// snippet_accessing_1
url_view u( "https://user:pass@example.com:443/path/to/my%2dfile.txt?id=42&name=John%20Doe+Jingleheimer%2DSchmidt#page%20anchor" );
assert(u.scheme() == "https");
assert(u.authority().buffer() == "user:pass@example.com:443");
assert(u.userinfo() == "user:pass");
assert(u.user() == "user");
assert(u.password() == "pass");
assert(u.host() == "example.com");
assert(u.port() == "443");
assert(u.path() == "/path/to/my-file.txt");
assert(u.query() == "id=42&name=John Doe Jingleheimer-Schmidt");
assert(u.fragment() == "page anchor");
----
URL paths can be further divided into path segments with
the function `url_view::segments`.
Although URL query strings are often used to represent key/value pairs, this
interpretation is not defined by `rfc3986`.
interpretation is not defined by https://tools.ietf.org/html/rfc3986[rfc3986,window=blank_].
Users can treat the query as a single entity.
`url_view` provides the function
`url_view::params` to extract this view
of key/value pairs.
// [table [[Code][Output]] [[
// [c++]
// [snippet_accessing_1b]
// ][
// [teletype]
// ```
// path
// to
// my-file.txt
//
// id: 42
// name: John Doe Jingleheimer-Schmidt
// ```
// ]]]
[cols="a,a"]
|===
// Headers
|Code|Output
// Row 1, Column 1
|
// snippet_accessing_1b
[source,cpp]
----
for (auto seg: u.segments())
std::cout << seg << "\n";
std::cout << "\n";
for (auto param: u.params())
std::cout << param.key << ": " << param.value << "\n";
std::cout << "\n";
----
// Row 1, Column 2
|
[source]
----
path
to
my-file.txt
id: 42
name: John Doe Jingleheimer-Schmidt
----
|===
These functions return views referring to substrings and sub-ranges
of the underlying URL.
@@ -148,33 +192,44 @@ By simply referencing the relevant portion of the URL string internally,
its components can represent percent-decoded strings and be converted
to other types without any previous memory allocation.
// snippet_token_1
[source,cpp]
----
// snippet_token_1
std::string h = u.host();
assert(h == "example.com");
----
A special `string_token` type can also be used to specify how a portion of the URL should be encoded and returned.
// snippet_token_2
[source,cpp]
----
// snippet_token_2
std::string h = "host: ";
u.host(string_token::append_to(h));
assert(h == "host: example.com");
----
These functions might also return empty strings
// snippet_accessing_2a
[source,cpp]
----
// snippet_accessing_2a
url_view u1 = parse_uri( "http://www.example.com" ).value();
assert(u1.fragment().empty());
assert(!u1.has_fragment());
----
for both empty and absent components
// snippet_accessing_2b
[source,cpp]
----
// snippet_accessing_2b
url_view u2 = parse_uri( "http://www.example.com/#" ).value();
assert(u2.fragment().empty());
assert(u2.has_fragment());
----
@@ -187,40 +242,80 @@ When applicable, the encoded components can also be directly
accessed through a `string_view` without any
need to allocate memory:
// [table [[Code][Output]] [[
// [c++]
// [snippet_accessing_4]
// ][
// [teletype]
// ```
// url : https://user:pass@example.com:443/path/to/my%2dfile.txt?id=42&name=John%20Doe+Jingleheimer%2DSchmidt#page%20anchor
// scheme : https
// authority : user:pass@example.com:443
// userinfo : user:pass
// user : user
// password : pass
// host : example.com
// port : 443
// path : /path/to/my%2dfile.txt
// query : id=42&name=John%20Doe+Jingleheimer%2DSchmidt
// fragment : page%20anchor
// ```
// ]]]
[cols="a,a"]
|===
// Headers
|Code|Output
=== Percent-Encoding
// Row 1, Column 1
|
// snippet_accessing_4
[source,cpp]
----
std::cout <<
"url : " << u << "\n"
"scheme : " << u.scheme() << "\n"
"authority : " << u.encoded_authority() << "\n"
"userinfo : " << u.encoded_userinfo() << "\n"
"user : " << u.encoded_user() << "\n"
"password : " << u.encoded_password() << "\n"
"host : " << u.encoded_host() << "\n"
"port : " << u.port() << "\n"
"path : " << u.encoded_path() << "\n"
"query : " << u.encoded_query() << "\n"
"fragment : " << u.encoded_fragment() << "\n";
----
// Row 1, Column 2
|
[source]
----
url : https://user:pass@example.com:443/path/to/my%2dfile.txt?id=42&name=John%20Doe+Jingleheimer%2DSchmidt#page%20anchor
scheme : https
authority : user:pass@example.com:443
userinfo : user:pass
user : user
password : pass
host : example.com
port : 443
path : /path/to/my%2dfile.txt
query : id=42&name=John%20Doe+Jingleheimer%2DSchmidt
fragment : page%20anchor
----
|===
== Percent-Encoding
An instance of `decode_view` provides a number of functions
to persist a decoded string:
// [table [[Code][Output]] [[
// [c++]
// [snippet_decoding_1]
// ][
// [teletype]
// ```
// id=42&name=John Doe Jingleheimer-Schmidt
// ```
// ]]]
[cols="a,a"]
|===
// Headers
|Code|Output
// Row 1, Column 1
|
// snippet_decoding_1
[source,cpp]
----
decode_view dv("id=42&name=John%20Doe%20Jingleheimer%2DSchmidt");
std::cout << dv << "\n";
----
// Row 1, Column 2
|
[source]
----
id=42&name=John Doe Jingleheimer-Schmidt
----
|===
`decode_view` and its decoding functions are designed to
perform no memory allocations unless the algorithm where its being
@@ -243,15 +338,32 @@ If `u2.host()` returned a value type, then two memory allocations
would be necessary for this operation. Another common use case
is converting URL path segments into filesystem paths:
// [table [[Code][Output]] [[
// [c++]
// [snippet_decoding_3]
// ][
// [teletype]
// ```
// path: "path/to/my-file.txt"
// ```
// ]]]
[cols="a,a"]
|===
// Headers
|Code|Output
// Row 1, Column 1
|
// snippet_decoding_3
[source,cpp]
----
boost::filesystem::path p;
for (auto seg: u.segments())
p.append(seg.begin(), seg.end());
std::cout << "path: " << p << "\n";
----
// Row 1, Column 2
|
[source]
----
path: "path/to/my-file.txt"
----
|===
In this example, only the internal allocations of
`filesystem::path` need to happen. In many common
@@ -260,9 +372,21 @@ such as finding the appropriate route for a URL
in a web server:
// snippet_decoding_4a
[source,cpp]
----
// snippet_decoding_4a
auto match = [](
std::vector<std::string> const& route,
url_view u)
{
auto segs = u.segments();
if (route.size() != segs.size())
return false;
return std::equal(
route.begin(),
route.end(),
segs.begin());
};
----
@@ -270,14 +394,20 @@ This allows us to easily match files in the document
root directory of a web server:
// snippet_decoding_4b
[source,cpp]
----
// snippet_decoding_4b
std::vector<std::string> route =
{"community", "reviews.html"};
if (match(route, u))
{
handle_route(route, u);
}
----
// [#compound-elements]
=== Compound elements
== Compound elements
The path and query parts of the URL are treated specially by the library.
While they can be accessed as individual encoded strings, they can also be
@@ -286,16 +416,34 @@ accessed through special view types.
This code calls
`encoded_segments`
to obtain the path segments as a container that returns encoded strings:
// [table [[Code][Output]] [[
// [c++]
// [snippet_compound_elements_1]
// ][
// ```
// path
// to
// my-file.txt
// ```
// ]]]
[cols="a,a"]
|===
// Headers
|Code|Output
// Row 1, Column 1
|
// snippet_compound_elements_1
[source,cpp]
----
segments_encoded_view segs = u.encoded_segments();
for( auto v : segs )
{
std::cout << v << "\n";
}
----
// Row 1, Column 2
|[source,cpp]
----
path
to
my-file.txt
----
|===
As with other `url_view` functions which return encoded strings, the encoded
segments container does not allocate memory. Instead it returns views to the
@@ -304,30 +452,68 @@ corresponding portions of the underlying encoded buffer referenced by the URL.
As with other library functions, `decode_view` permits accessing
elements of composed elements while avoiding memory allocations entirely:
// [table [[Code][Output]] [[
// [c++]
// [snippet_encoded_compound_elements_1]
// ][
// [teletype]
// ```
// path
// to
// my-file.txt
// ```
// ]][[
// [c++]
// [snippet_encoded_compound_elements_2]
// ][
// [teletype]
// ```
// key = id, value = 42
// key = name, value = John Doe
// ```
// ]]]
[cols="a,a"]
|===
// Headers
|Code|Output
// Row 1, Column 1
|
// snippet_encoded_compound_elements_1
[source,cpp]
----
segments_encoded_view segs = u.encoded_segments();
for( pct_string_view v : segs )
{
decode_view dv = *v;
std::cout << dv << "\n";
}
----
// Row 1, Column 2
|
[source]
----
path
to
my-file.txt
----
// Row 2, Column 1
|
// snippet_encoded_compound_elements_2
[source,cpp]
----
params_encoded_view params_ref = u.encoded_params();
for( auto v : params_ref )
{
decode_view dk(v.key);
decode_view dv(v.value);
std::cout <<
"key = " << dk <<
", value = " << dv << "\n";
}
----
// Row 2, Column 2
|
[source]
----
key = id, value = 42
key = name, value = John Doe
----
|===
=== Modifying
== Modifying
The library provides the containers `url` and `static_url` which supporting
modification of the URL contents. A `url` or `static_url` must be constructed
@@ -338,9 +524,10 @@ character buffer, the `url` container uses the default allocator to
control a resizable character buffer which it owns.
// snippet_quicklook_modifying_1
[source,cpp]
----
// snippet_quicklook_modifying_1
url u = parse_uri( s ).value();
----
@@ -348,9 +535,10 @@ On the other hand, a `static_url` has fixed-capacity storage and does
not require dynamic memory allocations.
// snippet_quicklook_modifying_1b
[source,cpp]
----
// snippet_quicklook_modifying_1b
static_url<1024> su = parse_uri( s ).value();
----
@@ -361,17 +549,19 @@ constructible, and equality comparable. They support all the inspection function
Changing the scheme is easy:
// snippet_quicklook_modifying_2
[source,cpp]
----
// snippet_quicklook_modifying_2
u.set_scheme( "https" );
----
Or we can use a predefined constant:
// snippet_quicklook_modifying_3
[source,cpp]
----
// snippet_quicklook_modifying_3
u.set_scheme_id( scheme::https ); // equivalent to u.set_scheme( "https" );
----
@@ -386,29 +576,62 @@ It is not possible for a `url` to hold syntactically illegal text.
Modification functions return a reference to the object, so chaining
is possible:
// [table [[Code][Output]] [[
// [c++]
// [snippet_quicklook_modifying_4]
// ][
// [teletype]
// ```
// https://192.168.0.1:8080/path/to/my%2dfile.txt?id=42&name=John%20Doe#page%20anchor
// ```
// ]]]
[cols="a,a"]
|===
// Headers
|Code|Output
// Row 1, Column 1
|
// snippet_quicklook_modifying_4
[source,cpp]
----
u.set_host_ipv4( ipv4_address( "192.168.0.1" ) )
.set_port_number( 8080 )
.remove_userinfo();
std::cout << u << "\n";
----
// Row 1, Column 2
|
[source]
----
https://192.168.0.1:8080/path/to/my%2dfile.txt?id=42&name=John%20Doe#page%20anchor
----
|===
All non-const operations offer the strong exception safety guarantee.
The path segment and query parameter containers returned by a `url` offer
modifiable range functionality, using member functions of the container:
// [table [[Code][Output]] [[
// [c++]
// [snippet_quicklook_modifying_5]
// ][
// [teletype]
// ```
// https://192.168.0.1:8080/path/to/my%2dfile.txt?id=42&name=Vinnie%20Falco#page%20anchor
// ```
// ]]]
[cols="a,a"]
|===
// Headers
|Code|Output
// Row 1, Column 1
|
// snippet_quicklook_modifying_5
[source,cpp]
----
params_ref p = u.params();
p.replace(p.find("name"), {"name", "John Doe"});
std::cout << u << "\n";
----
// Row 1, Column 2
|
[source]
----
https://192.168.0.1:8080/path/to/my%2dfile.txt?id=42&name=Vinnie%20Falco#page%20anchor
----
|===

File diff suppressed because it is too large Load Diff

View File

@@ -8,21 +8,23 @@
//
== Formatting
= Formatting
Algorithms to format URLs construct a mutable URL by parsing and applying
arguments to a URL template. The following example uses the `format`
function to construct an absolute URL:
// snippet_format_1
[source,cpp]
----
// snippet_format_1
url u = format("{}://{}:{}/rfc/{}", "https", "www.ietf.org", 80, "rfc2396.txt");
assert(u.buffer() == "https://www.ietf.org:80/rfc/rfc2396.txt");
----
The rules for a format URL string are the same
as for a `std::format_string`, where replacement
as for a https://en.cppreference.com/w/cpp/utility/format/basic_format_string[`std::format_string`,window=blank_], where replacement
fields are delimited by curly braces. The URL
type is inferred from the format string.
@@ -32,9 +34,11 @@ applied and any invalid characters for that
formatted argument are percent-escaped:
// snippet_format_2
[source,cpp]
----
// snippet_format_2
url u = format("https://{}/{}", "www.boost.org", "Hello world!");
assert(u.buffer() == "https://www.boost.org/Hello%20world!");
----
@@ -45,16 +49,25 @@ characters are normalized to ensure the URL is
valid:
[source,cpp]
----
// snippet_format_3a
----
[source,cpp]
----
url u = format("{}:{}", "mailto", "someone@example.com");
assert(u.buffer() == "mailto:someone@example.com");
assert(u.scheme() == "mailto");
assert(u.path() == "someone@example.com");
----
// snippet_format_3b
[source,cpp]
----
url u = format("{}{}", "mailto:", "someone@example.com");
assert(u.buffer() == "mailto%3Asomeone@example.com");
assert(!u.has_scheme());
assert(u.path() == "mailto:someone@example.com");
assert(u.encoded_path() == "mailto%3Asomeone@example.com");
----
@@ -62,19 +75,24 @@ The function `format_to` can be used to format URLs
into any modifiable URL container.
// snippet_format_4
[source,cpp]
----
// snippet_format_4
static_url<50> u;
format_to(u, "{}://{}:{}/rfc/{}", "https", "www.ietf.org", 80, "rfc2396.txt");
assert(u.buffer() == "https://www.ietf.org:80/rfc/rfc2396.txt");
----
As with `std::format`, positional and named arguments are
As with https://en.cppreference.com/w/cpp/utility/format/format[`std::format`,window=blank_], positional and named arguments are
supported.
// snippet_format_5a
[source,cpp]
----
// snippet_format_5a
url u = format("{0}://{2}:{1}/{3}{4}{3}", "https", 80, "www.ietf.org", "abra", "cad");
assert(u.buffer() == "https://www.ietf.org:80/abracadabra");
----
@@ -82,21 +100,26 @@ The `arg` function can be used to associate names
with arguments:
// snippet_format_5b
[source,cpp]
----
// snippet_format_5b
url u = format("https://example.com/~{username}", arg("username", "mark"));
assert(u.buffer() == "https://example.com/~mark");
----
A second overload based on `std::initializer_list`
A second overload based on https://en.cppreference.com/w/cpp/utility/initializer_list[`std::initializer_list`,window=blank_]
is provided for both `format` and `format_to`.
These overloads can help with lists of named
arguments:
// snippet_format_5c
[source,cpp]
----
// snippet_format_5c
boost::core::string_view fmt = "{scheme}://{host}:{port}/{dir}/{file}";
url u = format(fmt, {{"scheme", "https"}, {"port", 80}, {"host", "example.com"}, {"dir", "path/to"}, {"file", "file.txt"}});
assert(u.buffer() == "https://example.com:80/path/to/file.txt");
----

View File

@@ -8,17 +8,17 @@
//
== URLs
= URLs
A URL, short for "Uniform Resource Locator," is a compact string
of characters identifying an abstract or physical resource.
It has these five parts, with may be optional or disallowed
depending on the context:
// [$url/images/PartsDiagram.svg]
image:PartsDiagram.svg[]
Each part's syntax is defined by a set of production rules in
`rfc3986`. All valid URLs conform to this grammar, also called
https://tools.ietf.org/html/rfc3986[rfc3986,window=blank_]. All valid URLs conform to this grammar, also called
the "generic syntax." Here is an example URL which describes a
file and its location on a network host:
@@ -31,40 +31,55 @@ https://www.example.com/path/to/file.txt?userid=1001&pages=3&results=full#page1
The parts and their corresponding text is as follows:
// [table Example Parts [
// [Part]
// [Text]
// ][
// [[link url.urls.containers.scheme ['scheme]]]
// ["https"]
// ][
// [[link url.urls.containers.authority ['authority]]]
// ["www.example.com"]
// ][
// [[link url.urls.containers.path ['path]]]
// ["/path/to/file.txt"]
// ][
// [[link url.urls.containers.query ['query]]]
// ["userid=1001&pages=3&results=full"]
// ][
// [[link url.urls.containers.fragment ['fragment]]]
// ["page1"]
// ]]
[cols="a,a"]
|===
// Headers
|Part|Text
// Row 1, Column 1
|__scheme__
// Row 1, Column 2
|"https"
// Row 2, Column 1
|__authority__
// Row 2, Column 2
|"www.example.com"
// Row 3, Column 1
|__path__
// Row 3, Column 2
|"/path/to/file.txt"
// Row 4, Column 1
|__query__
// Row 4, Column 2
|"userid=1001&pages=3&results=full"
// Row 5, Column 1
|__fragment__
// Row 5, Column 2
|"page1"
|===
The production rule for the example above is called a __URI__,
which can contain all five parts. The specification using
https://datatracker.ietf.org/doc/html/rfc2234[__ABNF__,window=blank_]
https://datatracker.ietf.org/doc/html/rfc2234[__ABNF notation__,window=blank_]
is:
```
URI = scheme ":" hier-part // [ "?" query ] // [ "#" fragment ]
[source]
----
URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
hier-part = "//" authority path-abempty
/ path-absolute
/ path-rootless
/ path-empty
```
----
In this notation, the square brackets ("\// [" and "\]") denote optional
elements, quoted text represents character literals, and slashes are
@@ -80,7 +95,7 @@ __URI-reference__. These are discussed in greater depth later.
=== Scheme
== Scheme
The most important part is the __scheme__, whose production rule is:
@@ -98,28 +113,44 @@ https://en.wikipedia.org/wiki/Internet_Assigned_Numbers_Authority[Internet Assig
Here are some registered schemes and their corresponding
specifications:
// [table Public Schemes [
// [Scheme]
// [Specification]
// ][
// [[*http]]
// [[@https://datatracker.ietf.org/doc/html/rfc7230#section-2.7.1 http URI Scheme (rfc7230)]]
// ][
// [[*magnet]]
// [[@https://en.wikipedia.org/wiki/Magnet_URI_scheme Magnet URI scheme]]
// ][
// [[*mailto]]
// [[@https://datatracker.ietf.org/doc/html/rfc6068 The 'mailto' URI Scheme (rfc6068)]]
// ][
// [[*payto]]
// [[@https://datatracker.ietf.org/doc/html/rfc8905 The 'payto' URI Scheme for Payments (rfc8905)]]
// ][
// [[*telnet]]
// [[@https://datatracker.ietf.org/doc/html/rfc4248 The telnet URI Scheme (rfc4248)]]
// ][
// [[*urn]]
// [[@https://datatracker.ietf.org/doc/html/rfc2141 URN Syntax]]
// ]]
[cols="a,a"]
|===
// Headers
|Scheme|Specification
// Row 1, Column 1
|**http**
// Row 1, Column 2
|https://datatracker.ietf.org/doc/html/rfc7230#section-2.7.1[http URI Scheme (rfc7230),window=blank_]
// Row 2, Column 1
|**magnet**
// Row 2, Column 2
|https://en.wikipedia.org/wiki/Magnet_URI_scheme[Magnet URI scheme,window=blank_]
// Row 3, Column 1
|**mailto**
// Row 3, Column 2
|https://datatracker.ietf.org/doc/html/rfc6068[The 'mailto' URI Scheme (rfc6068),window=blank_]
// Row 4, Column 1
|**payto**
// Row 4, Column 2
|https://datatracker.ietf.org/doc/html/rfc8905[The 'payto' URI Scheme for Payments (rfc8905),window=blank_]
// Row 4, Column 4
// Row 5, Column 1
|**telnet**
// Row 5, Column 2
|https://datatracker.ietf.org/doc/html/rfc4248[The telnet URI Scheme (rfc4248),window=blank_]
// Row 6, Column 1
|**urn**
// Row 6, Column 2
|https://datatracker.ietf.org/doc/html/rfc2141[URN Syntax,window=blank_]
|===
Private schemes are possible, defined by organizations to enumerate internal
resources such as documents or physical devices, or to facilitate the operation
@@ -130,19 +161,28 @@ that private does not imply secret; some private schemes such as Amazon's "s3"
have publicly available specifications and are quite popular. Here are some
examples:
// [table Private Schemes [
// [Scheme]
// [Specification]
// ][
// [[*app]]
// [[@https://www.w3.org/TR/app-uri/ app: URL Scheme]]
// ][
// [[*odbc]]
// [[@https://datatracker.ietf.org/doc/html/draft-patrick-lambert-odbc-uri-scheme ODBC URI Scheme]]
// ][
// [[*slack]]
// [[@https://api.slack.com/reference/deep-linking Reference: Deep linking into Slack]]
// ]]
[cols="a,a"]
|===
// Headers
|Scheme|Specification
// Row 1, Column 1
|**app**
// Row 1, Column 2
|https://www.w3.org/TR/app-uri/[app: URL Scheme,window=blank_]
// Row 2, Column 1
|**odbc**
// Row 2, Column 2
|https://datatracker.ietf.org/doc/html/draft-patrick-lambert-odbc-uri-scheme[ODBC URI Scheme,window=blank_]
// Row 3, Column 1
|**slack**
// Row 3, Column 2
|https://api.slack.com/reference/deep-linking[Reference: Deep linking into Slack,window=blank_]
|===
In some cases the scheme is implied by the surrounding context and
therefore omitted. Here is a complete HTTP/1.1 GET request for the
@@ -165,47 +205,66 @@ https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.1[HTTP specification,w
thusly:
```
origin-form = absolute-path // [ "?" query ]
[source]
----
origin-form = absolute-path [ "?" query ]
absolute-path = 1*( "/" segment )
```
----
[NOTE]
====
All URLs have a scheme, whether it is explicit or implicit.
The scheme determines what the rest of the URL means.
====
// [note
// All URLs have a scheme, whether it is explicit or implicit.
// The scheme determines what the rest of the URL means.
// ]
Here are some more examples of URLs using various schemes (and one example
of something that is not a URL):
// [table Scheme Examples [
// [URL]
// [Notes]
// ][
// [`https://www.boost.org/index.html`]
// [Hierarchical URL with `https` protocol. Resource in the HTTP protocol.]
// ][
// [`ftp://host.dom/etc/motd`]
// [Hierarchical URL with `ftp` scheme. Resource in the FTP protocol.]
// ][
// [`urn:isbn:045145052`]
// [Opaque URL with `urn` scheme. Identifies `isbn` resource.]
// ][
// [`mailto:person@example.com`]
// [Opaque URL with `mailto` scheme. Identifies e-mail address.]
// ][
// [`index.html`]
// [URL reference. Missing scheme and authority.]
// ][
// [`www.boost.org`]
// [A Protocol-Relative Link (PRL). [*Not a URL].]
// ]]
[cols="a,a"]
|===
// Headers
|URL|Notes
// Row 1, Column 1
|`pass:[https://www.boost.org/index.html]`
// Row 1, Column 2
|Hierarchical URL with `https` protocol. Resource in the HTTP protocol.
// Row 2, Column 1
|`pass:[ftp://host.dom/etc/motd]`
// Row 2, Column 2
|Hierarchical URL with `ftp` scheme. Resource in the FTP protocol.
// Row 3, Column 1
|`urn:isbn:045145052`
// Row 3, Column 2
|Opaque URL with `urn` scheme. Identifies `isbn` resource.
// Row 4, Column 1
|`mailto:person@example.com`
// Row 4, Column 2
|Opaque URL with `mailto` scheme. Identifies e-mail address.
// Row 5, Column 1
|`index.html`
// Row 5, Column 2
|URL reference. Missing scheme and authority.
// Row 6, Column 1
|`www.boost.org`
// Row 6, Column 2
|A Protocol-Relative Link (PRL). **Not**.
|===
=== Authority
== Authority
The authority determines how a resource can be accessed.
It contains two parts: the
@@ -218,9 +277,11 @@ which identify a communication endpoint having dominion
over the resource described in the remainder of the URL.
This is the ABNF specification for the authority part:
```
authority = // [ user [ ":" password ] "@" ] host // [ ":" port ]
```
[source]
----
authority = [ user [ ":" password ] "@" ] host [ ":" port ]
----
The combination of user and optional password is called the
__userinfo__.
@@ -235,7 +296,7 @@ https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.3[__port__,window=blan
which identify a communication endpoint having dominion
over the resource described in the remainder of the URL.
// [$url/images/AuthorityDiagram.svg]
image:AuthorityDiagram.svg[]
Some observations:
@@ -247,28 +308,42 @@ Some observations:
The host subcomponent represents where resources
are located.
// [note
// Note that if an authority is present, the host is always
// defined even if it is the empty string (corresponding
// to a zero-length ['reg-name] in the BNF).
//
// [snippet_parsing_authority_10a]
// ]
[NOTE]
====
Note that if an authority is present, the host is always
defined even if it is the empty string (corresponding
to a zero-length __reg-name__ in the BNF).
// snippet_parsing_authority_10a
[source,cpp]
----
url_view u( "https:///path/to_resource" );
assert( u.has_authority() );
assert( u.authority().buffer().empty() );
assert( u.path() == "/path/to_resource" );
----
====
The authority component also influences how we should
interpret the URL path. If the authority is present,
the path component must either be empty or begin with
a slash.
// [note Although the specification allows the format `username:password`,
// the password component should be used with care.
//
// It is not recommended to transfer password data through URLs
// unless this is an empty string indicating no password.]
[NOTE]
====
Although the specification allows the format `username:password`,
the password component should be used with care.
It is not recommended to transfer password data through URLs
unless this is an empty string indicating no password.
====
=== Containers
== Containers
This library provides the following containers, which
are capable of storing any possible URL:
@@ -299,14 +374,14 @@ use with URLs.
// StringToken
// Percent Encoding
// [include 3.1.parsing.qbk]
// [include 3.2.containers.qbk]
// [include 3.3.segments.qbk]
// [include 3.4.params.qbk]
// [include 3.5.normalization.qbk]
// [include 3.6.stringtoken.qbk]
// [include 3.7.percent-encoding.qbk]
// [include 3.8.formatting.qbk]

View File

@@ -8,7 +8,7 @@
//
== Normalization
= Normalization
Normalization allows us to determine if two URLs refer to the same
resource. URLs comparisons serve the same purpose, where two strings
@@ -40,20 +40,26 @@ of producing false negatives:
Simple String Comparison can be performed by accessing the underlying
buffer of URLs:
// snippet_normalizing_1
[source,cpp]
----
// snippet_normalizing_1
url_view u1("https://www.boost.org/index.html");
url_view u2("https://www.boost.org/doc/../index.html");
assert(u1.buffer() != u2.buffer());
----
By only considering the rules of `rfc3986`, Simple String
By only considering the rules of https://tools.ietf.org/html/rfc3986[rfc3986,window=blank_], Simple String
Comparison fails to identify the URLs above point to the same
resource. The comparison operators implement Syntax-Based
Normalization, which implements the rules defined by `rfc3986`.
Normalization, which implements the rules defined by https://tools.ietf.org/html/rfc3986[rfc3986,window=blank_].
// snippet_normalizing_2
[source,cpp]
----
// snippet_normalizing_2
url_view u1("https://www.boost.org/index.html");
url_view u2("https://www.boost.org/doc/../index.html");
assert(u1 == u2);
----
@@ -66,13 +72,18 @@ same underlying representation. In other words, Simple String
Comparison of two normalized URLs is equivalent to
Syntax-Based Normalization.
// snippet_normalizing_3
[source,cpp]
----
// snippet_normalizing_3
url_view u1("https://www.boost.org/index.html");
url u2("https://www.boost.org/doc/../index.html");
assert(u1 == u2);
u2.normalize();
assert(u1.buffer() == u2.buffer());
----
Normalization uses the following definitions of `rfc3986`
Normalization uses the following definitions of https://tools.ietf.org/html/rfc3986[rfc3986,window=blank_]
to minimize false negatives:
* Case Normalization: percent-encoding triplets are normalized to use uppercase letters
@@ -81,9 +92,12 @@ to minimize false negatives:
The following example normalizes the percent-encoding and path segments of a URL:
// snippet_normalizing_4
[source,cpp]
----
// snippet_normalizing_4
url u("https://www.boost.org/doc/../%69%6e%64%65%78%20file.html");
u.normalize();
assert(u.buffer() == "https://www.boost.org/index%20file.html");
----
@@ -92,9 +106,34 @@ for Scheme-Based and Protocol-Based Normalization. One common
scheme-specific rule is ignoring the default port for that
scheme and empty absolute paths:
// snippet_normalizing_5
[source,cpp]
----
// snippet_normalizing_5
auto normalize_http_url =
[](url& u)
{
u.normalize();
if (u.port() == "80" ||
u.port().empty())
u.remove_port();
if (u.has_authority() &&
u.encoded_path().empty())
u.set_path_absolute(true);
};
url u1("https://www.boost.org");
normalize_http_url(u1);
url u2("https://www.boost.org/");
normalize_http_url(u2);
url u3("https://www.boost.org:/");
normalize_http_url(u3);
url u4("https://www.boost.org:80/");
normalize_http_url(u4);
assert(u1.buffer() == "https://www.boost.org/");
assert(u2.buffer() == "https://www.boost.org/");
assert(u3.buffer() == "https://www.boost.org/");
assert(u4.buffer() == "https://www.boost.org/");
----

View File

@@ -10,12 +10,12 @@
== Params
= Params
While the query is specified as a plain string, it is usually
interpreted as a set of key-value pairs commonly referred to as
https://en.wikipedia.org/wiki/Query_string[__URL__,window=blank_],
although here we use the term __query__ or __params__
https://en.wikipedia.org/wiki/Query_string[__URL Parameters__,window=blank_],
although here we use the term __query parameters__ or __params__
for short. There is no official, standard specification of the
query parameters format, but the W3C recommendations and HTML 5
have this to say:
@@ -45,35 +45,41 @@ Like the path, the library permits access to the params as
using these separate, bidirectional view types which reference
the underlying URL:
// [table Params Types [
// [Type]
// [Accessor]
// [Description]
// ][
// [__params_view__]
// [[link url.ref.boost__urls__url_view_base.params `params`]]
// [
// A read-only range of decoded params.
// ]
// ][
// [__params_ref__]
// [[link url.ref.boost__urls__url_base.params `params`]]
// [
// A modifiable range of decoded params.
// ]
// ][
// [__params_encoded_view__]
// [[link url.ref.boost__urls__url_view_base.encoded_params `encoded_params`]]
// [
// A read-only range of params.
// ]
// ][
// [__params_encoded_ref__]
// [[link url.ref.boost__urls__url_base.encoded_params `encoded_params`]]
// [
// A modifiable range of params.
// ]
// ]]
[cols="a,a,a"]
|===
// Headers
|Type|Accessor|Description
// Row 1, Column 1
|`params_view`
// Row 1, Column 2
|`params`
// Row 1, Column 3
|A read-only range of decoded params.
// Row 2, Column 1
|`params_ref`
// Row 2, Column 2
|`params`
// Row 2, Column 3
|A modifiable range of decoded params.
// Row 3, Column 1
|`params_encoded_view`
// Row 3, Column 2
|`encoded_params`
// Row 3, Column 3
|A read-only range of params.
// Row 4, Column 1
|`params_encoded_ref`
// Row 4, Column 2
|`encoded_params`
// Row 4, Column 3
|A modifiable range of params.
|===
A param always has a key, even if it is the empty string.
The value is optional; an empty string is distinct from
@@ -81,34 +87,39 @@ no value. To represent individual params the library uses
these types, distinguished by their ownership model and
whether or not percent-escapes are possible:
// [table Param Types [
// [Type]
// [String Type]
// [Description]
// ][
// [__param__]
// [__std_string__]
// [
// A key-value pair with ownership of the strings.
// This can be used to hold decoded strings, or to
// allow the caller to take ownership of a param
// by making a copy.
// ]
// ][
// [__param_view__]
// [__string_view__]
// [
// A key-value pair without percent-escapes,
// referencing externally managed character buffers.
// ]
// ][
// [__param_pct_view__]
// [__pct_string_view__]
// [
// A key-value pair which may contain percent-escapes,
// referencing externally managed character buffers.
// ]
// ]]
[cols="a,a,a"]
|===
// Headers
|Type|String Type|Description
// Row 1, Column 1
|`param`
// Row 1, Column 2
|https://en.cppreference.com/w/cpp/string/basic_string[`std::string`,window=blank_]
// Row 1, Column 3
|A key-value pair with ownership of the strings.
This can be used to hold decoded strings, or to
allow the caller to take ownership of a param
by making a copy.
// Row 2, Column 1
|`param_view`
// Row 2, Column 2
|`string_view`
// Row 2, Column 3
|A key-value pair without percent-escapes,
referencing externally managed character buffers.
// Row 3, Column 1
|`param_pct_view`
// Row 3, Column 2
|`pct_string_view`
// Row 3, Column 3
|A key-value pair which may contain percent-escapes,
referencing externally managed character buffers.
|===
Param types can be constructed from initializer lists,
allowing for convenient notation. To represent a missing
@@ -118,32 +129,49 @@ or `nullptr` may be used. This table shows some examples
of initializer lists used to construct a param type, and
the resulting data members:
// [table Param Initializers [
// [Statement]
// [`qp.key`]
// [`qp.value`]
// [`qp.has_value`]
// ][
// [`param qp = { "first", "John" };`]
// [`"First"`]
// [`"John"`]
// [`true`]
// ][
// [`param qp = { "first", "" };`]
// [`"First"`]
// [`""`]
// [`true`]
// ][
// [`param qp = { "first", no_value };`]
// [`"First"`]
// [`""`]
// [`false`]
// ][
// [`param qp = { "", "Doe" };`]
// [`""`]
// [`"Doe"`]
// [`true`]
// ]]
[cols="a,a,a,a"]
|===
// Headers
|Statement|`qp.key`|`qp.value`|`qp.has_value`
// Row 1, Column 1
|`param qp = { "first", "John" };`
// Row 1, Column 2
|`"First"`
// Row 1, Column 3
|`"John"`
// Row 1, Column 4
|`true`
// Row 2, Column 1
|`param qp = { "first", "" };`
// Row 2, Column 2
|`"First"`
// Row 2, Column 3
|`""`
// Row 2, Column 4
|`true`
// Row 3, Column 1
|`param qp = { "first", no_value };`
// Row 3, Column 2
|`"First"`
// Row 3, Column 3
|`""`
// Row 3, Column 4
|`false`
// Row 4, Column 1
|`param qp = { "", "Doe" };`
// Row 4, Column 2
|`""`
// Row 4, Column 3
|`"Doe"`
// Row 4, Column 4
|`true`
|===
To understand the relationship between the query and the
resulting range of params, first we define this
@@ -151,9 +179,17 @@ function `parms` which returns a list of params
corresponding to the elements in a container
of params:
// code_container_5_1
[source,cpp]
----
// code_container_5_1
auto parms( core::string_view s ) -> std::list< param >
{
url_view u( s );
std::list< param > seq;
for( auto qp : u.params() )
seq.push_back( qp );
return seq;
}
----
@@ -161,28 +197,43 @@ In the table below we show the result of invoking `parms` with
different queries. This demonstrates how the syntax of the query
maps to the parameter structure:
// [table Params Sequences [
// [s]
// [`parms( s )`]
// ][
// [`"?first=John&last=Doe"`]
// [`{ { "first", "John" }, { "last", "Doe" } }`]
// ][
// [`"?id=42&unsorted"`]
// [`{ { "id", "42" }, { "last", no_value } }`]
// ][
// [`"?col=cust&row="`]
// [`{ { "col", "cust" }, { "row", "" } }`]
// ][
// [`"?justify=left&"`]
// [`{ { "justify", "left" }, { "", no_value } }`]
// ][
// [`"?"`]
// [`{ { "", no_value } }`]
// ][
// [`""`]
// [`{ }`]
// ]]
[cols="a,a"]
|===
// Headers
|s|`parms( s )`
// Row 1, Column 1
|`"?first=John&last=Doe"`
// Row 1, Column 2
|`{ { "first", "John" }, { "last", "Doe" } }`
// Row 2, Column 1
|`"?id=42&unsorted"`
// Row 2, Column 2
|`{ { "id", "42" }, { "last", no_value } }`
// Row 3, Column 1
|`"?col=cust&row="`
// Row 3, Column 2
|`{ { "col", "cust" }, { "row", "" } }`
// Row 4, Column 1
|`"?justify=left&"`
// Row 4, Column 2
|`{ { "justify", "left" }, { "", no_value } }`
// Row 5, Column 1
|`"?"`
// Row 5, Column 2
|`{ { "", no_value } }`
// Row 6, Column 1
|`""`
// Row 6, Column 2
|`{ }`
|===
It may be surprising that an empty query string ("?")
produces a sequence with one empty param. This is by

View File

@@ -8,7 +8,7 @@
//
== Parsing
= Parsing
Algorithms which parse URLs return a view which references the
underlying character buffer without taking ownership, avoiding
@@ -17,9 +17,10 @@ string literal containing a
https://datatracker.ietf.org/doc/html/rfc3986#section-3[__URI__,window=blank_]:
// code_urls_parsing_1
[source,cpp]
----
// code_urls_parsing_1
boost::core::string_view s = "https://user:pass@example.com:443/path/to/my%2dfile.txt?id=42&name=John%20Doe+Jingleheimer%2DSchmidt#page%20anchor";
----
@@ -42,9 +43,10 @@ https://datatracker.ietf.org/doc/html/rfc3986#section-4.1[__URI-reference__,wind
grammar, throwing an exception upon failure. The following two statements
are equivalent:
// code_urls_parsing_2
[source,cpp]
----
// code_urls_parsing_2
boost::system::result<url_view> r = parse_uri( s );
----
@@ -54,49 +56,73 @@ There are several varieties of URLs, and depending on the use-case a
particular grammar may be needed. In the target of an HTTP GET request
for example, the scheme and fragment are omitted. This corresponds to the
https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.1[__origin-form__,window=blank_]
production rule described in `rfc7230`. The function
production rule described in https://tools.ietf.org/html/rfc7230[rfc7230,window=blank_]. The function
`parse_origin_form`
is suited for this purpose. All the URL parsing functions are listed here:
// [table Parsing Functions [
// [Function]
// [Grammar]
// [Example]
// [Notes]
// ][
// [[link url.ref.boost__urls__parse_absolute_uri `parse_absolute_uri`]]
// [[@https://datatracker.ietf.org/doc/html/rfc3986#section-4.3 ['absolute-URI]]]
// [[teletype]`http://www.boost.org/index.html?field=value`]
// [No fragment]
// ][
// [[link url.ref.boost__urls__parse_origin_form `parse_origin_form`]]
// [[@https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.1 ['origin-form]]]
// [[teletype]`/index.html?field=value`]
// [Used in HTTP]
// ][
// [[link url.ref.boost__urls__parse_relative_ref `parse_relative_ref`]]
// [[@https://datatracker.ietf.org/doc/html/rfc3986#section-4.2 ['relative-ref]]]
// [[teletype]`//www.boost.org/index.html?field=value#downloads`]
// []
// ][
// [[link url.ref.boost__urls__parse_uri `parse_uri`]]
// [[@https://datatracker.ietf.org/doc/html/rfc3986#section-3 ['URI]]]
// [[teletype]`http://www.boost.org/index.html?field=value#downloads`]
// []
// ][
// [[link url.ref.boost__urls__parse_uri_reference `parse_uri_reference`]]
// [[@https://datatracker.ietf.org/doc/html/rfc3986#section-4.1 ['URI-reference]]]
// [[teletype]`http://www.boost.org/index.html`]
// [Any ['URI] or ['relative-ref]]
// ]]
[cols="a,a,a,a"]
|===
// Headers
|Function|Grammar|Example|Notes
// Row 1, Column 1
|`parse_absolute_uri`
// Row 1, Column 2
|https://datatracker.ietf.org/doc/html/rfc3986#section-4.3[__absolute-URI__,window=blank_]
// Row 1, Column 3
|`pass:[http://www.boost.org/index.html?field=value]`
// Row 1, Column 4
|No fragment
// Row 2, Column 1
|`parse_origin_form`
// Row 2, Column 2
|https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.1[__origin-form__,window=blank_]
// Row 2, Column 3
|`pass:[/index.html?field=value]`
// Row 2, Column 4
|Used in HTTP
// Row 3, Column 1
|`parse_relative_ref`
// Row 3, Column 2
|https://datatracker.ietf.org/doc/html/rfc3986#section-4.2[__relative-ref__,window=blank_]
// Row 3, Column 3
|`pass:[//www.boost.org/index.html?field=value#downloads]`
// Row 3, Column 4
|
// Row 4, Column 1
|`parse_uri`
// Row 4, Column 2
|https://datatracker.ietf.org/doc/html/rfc3986#section-3[__URI__,window=blank_]
// Row 4, Column 3
|`pass:[http://www.boost.org/index.html?field=value#downloads]`
// Row 4, Column 4
|
// Row 5, Column 1
|`parse_uri_reference`
// Row 5, Column 2
|https://datatracker.ietf.org/doc/html/rfc3986#section-4.1[__URI-reference__,window=blank_]
// Row 5, Column 3
|`pass:[http://www.boost.org/index.html]`
// Row 5, Column 4
|Any __URI__ or __relative-ref__
|===
The URL is stored in its serialized form. Therefore, it can
always be easily output, sent, or embedded as part of a
protocol:
// snippet_parsing_url_1bb
[source,cpp]
----
// snippet_parsing_url_1bb
url u = parse_uri_reference( "https://www.example.com/path/to/file.txt" ).value();
assert(u.encoded_path() == "/path/to/file.txt");
----
@@ -104,9 +130,12 @@ A `url` is an allocating container which owns its character buffer.
Upon construction from `url_view`, it allocates dynamic storage
to hold a copy of the string.
// snippet_parsing_url_1bc
[source,cpp]
----
// snippet_parsing_url_1bc
boost::system::result< url > rv = parse_uri_reference( "https://www.example.com/path/to/file.txt" );
static_assert( std::is_convertible< boost::system::result< url_view >, boost::system::result< url > >::value, "" );
----
@@ -114,13 +143,16 @@ A `static_url` is a container which owns its character buffer for
a URL whose maximum size is known. Upon construction from
`url_view`, it does not perform any dynamic memory allocations.
// snippet_parsing_url_1bd
[source,cpp]
----
// snippet_parsing_url_1bd
boost::system::result< static_url<1024> > rv = parse_uri_reference( "https://www.example.com/path/to/file.txt" );
static_assert( std::is_convertible< boost::system::result< static_url<1024> >, boost::system::result< url > >::value, "" );
----
=== Result Type
== Result Type
These functions have a return type which uses the `result` alias
template. This class allows the parsing algorithms to report
@@ -129,9 +161,20 @@ errors without referring to exceptions.
The functions `result::operator bool()` and `result::operator*`
can be used to check if the result contains an error.
// snippet_parsing_url_1
[source,cpp]
----
// snippet_parsing_url_1
boost::system::result< url > ru = parse_uri_reference( "https://www.example.com/path/to/file.txt" );
if ( ru )
{
url u = *ru;
assert(u.encoded_path() == "/path/to/file.txt");
}
else
{
boost::system::error_code e = ru.error();
handle_error(e);
}
----
@@ -140,9 +183,18 @@ error, `result::operator*` provides an unchecked alternative to get a value
from `result`. In contexts where it is acceptable to throw errors,
`result::value` can be used directly.
// snippet_parsing_url_1b
[source,cpp]
----
// snippet_parsing_url_1b
try
{
url u = parse_uri_reference( "https://www.example.com/path/to/file.txt" ).value();
assert(u.encoded_path() == "/path/to/file.txt");
}
catch (boost::system::system_error &e)
{
handle_error(e);
}
----

View File

@@ -8,61 +8,83 @@
//
== Percent Encoding
= Percent Encoding
=== Encoding
== Encoding
The `encode` can be used to percent-encode strings
with the specified `CharSet`.
with the specified __CharSet__.
// snippet_encoding_1
[source,cpp]
----
// snippet_encoding_1
std::string s = encode("hello world!", unreserved_chars);
assert(s == "hello%20world%21");
----
A few parameters, such as encoding spaces as plus (`+`), can be adjusted
with `encode_opts`:
// snippet_encoding_2
[source,cpp]
----
// snippet_encoding_2
encoding_opts opt;
opt.space_as_plus = true;
std::string s = encode("msg=hello world", pchars, opt);
assert(s == "msg=hello+world");
----
The result type of the function can also be specified via a `StringToken`
The result type of the function can also be specified via a __StringToken__
so that strings can be reused or appended.
// snippet_encoding_3
[source,cpp]
----
// snippet_encoding_3
std::string s;
encode("hello ", pchars, {}, string_token::assign_to(s));
encode("world", pchars, {}, string_token::append_to(s));
assert(s == "hello%20world");
----
We can also use `encoded_size` to
determine the required size before attempting to encode:
// snippet_encoding_4
[source,cpp]
----
// snippet_encoding_4
boost::core::string_view e = "hello world";
std::string s;
s.reserve(encoded_size(e, pchars));
encode(e, pchars, {}, string_token::assign_to(s));
assert(s == "hello%20world");
----
In other scenarios, strings can also be directly encoded into buffers:
// snippet_encoding_5
[source,cpp]
----
// snippet_encoding_5
boost::core::string_view e = "hello world";
std::string s;
s.resize(encoded_size(e, pchars));
encode(&s[0], s.size(), e, pchars);
assert(s == "hello%20world");
----
=== Validating
== Validating
The class `pct_string_view` represents a reference percent-encoded strings:
// snippet_encoding_6
[source,cpp]
----
// snippet_encoding_6
pct_string_view sv = "hello%20world";
assert(sv == "hello%20world");
----
@@ -75,9 +97,14 @@ To simply validate a string without recurring to exceptions, a `result`
can be returned with the
`make_pct_string_view`:
// snippet_encoding_7
[source,cpp]
----
// snippet_encoding_7
boost::system::result<pct_string_view> rs =
make_pct_string_view("hello%20world");
assert(rs.has_value());
pct_string_view sv = rs.value();
assert(sv == "hello%20world");
----
@@ -88,9 +115,13 @@ The modifying functions in classes such as `url` expect instances of
removes the responsibility of revalidating this information or throwing
exceptions from these functions:
// snippet_encoding_8
[source,cpp]
----
// snippet_encoding_8
pct_string_view s = "path/to/file";
url u;
u.set_encoded_path(s);
assert(u.buffer() == "path/to/file");
----
@@ -98,9 +129,12 @@ When exceptions are acceptable, a common pattern is to let a literal string
or other type convertible to `string_view` be implicitly converted to
`pct_string_view`.
// snippet_encoding_9
[source,cpp]
----
// snippet_encoding_9
url u;
u.set_encoded_path("path/to/file");
assert(u.buffer() == "path/to/file");
----
@@ -112,9 +146,13 @@ Reusing the validation guarantee is particularly useful when the
`pct_string_view` comes from another source where the data is also
ensured to be validated:
// snippet_encoding_10
[source,cpp]
----
// snippet_encoding_10
url_view uv("path/to/file");
url u;
u.set_encoded_path(uv.encoded_path());
assert(u.buffer() == "path/to/file");
----
@@ -124,15 +162,20 @@ does not to revalidate any information from
`encoded_path`
because these references are passed as `pct_string_view`.
=== Decode
== Decode
The class `pct_string_view` represents a reference percent-encoded strings.
`decode_view` is analogous to `pct_string_view`, with the main difference
that the underlying buffer always dereferences to decoded characters.
// snippet_encoding_11
[source,cpp]
----
// snippet_encoding_11
pct_string_view es("hello%20world");
assert(es == "hello%20world");
decode_view dv("hello%20world");
assert(dv == "hello world");
----
@@ -140,9 +183,15 @@ A `decode_view` can also be created from a `pct_string_view` with the
`operator*`.
The also gives us an opportunity to validate external strings:
// snippet_encoding_12
[source,cpp]
----
// snippet_encoding_12
boost::system::result<pct_string_view> rs =
make_pct_string_view("hello%20world");
assert(rs.has_value());
pct_string_view s = rs.value();
decode_view dv = *s;
assert(dv == "hello world");
----
@@ -150,9 +199,37 @@ This is particularly useful when the decoded string need to be accessed
for comparisons with no necessity to explicitly decoding the
string into a buffer:
// snippet_encoding_13
[source,cpp]
----
// snippet_encoding_13
url_view u =
parse_relative_ref("user/john%20doe/profile%20photo.jpg").value();
std::vector<std::string> route =
{"user", "john doe", "profile photo.jpg"};
auto segs = u.encoded_segments();
auto it0 = segs.begin();
auto end0 = segs.end();
auto it1 = route.begin();
auto end1 = route.end();
while (
it0 != end0 &&
it1 != end1)
{
pct_string_view seg0 = *it0;
decode_view dseg0 = *seg0;
boost::core::string_view seg1 = *it1;
if (dseg0 == seg1)
{
++it0;
++it1;
}
else
{
break;
}
}
bool route_match = it0 == end0 && it1 == end1;
assert(route_match);
----
@@ -162,9 +239,14 @@ can be used to decode the data into a buffer. Like the free-function
`encode`, decoding options and the string
token can be customized.
// snippet_encoding_14
[source,cpp]
----
// snippet_encoding_14
pct_string_view s = "user/john%20doe/profile%20photo.jpg";
std::string buf;
buf.resize(s.decoded_size());
s.decode({}, string_token::assign_to(buf));
assert(buf == "user/john doe/profile photo.jpg");
----

View File

@@ -10,42 +10,48 @@
== Segments
= Segments
Hierarchical schemes often interpret the path as a slash-delimited
sequence of percent-encoded strings called segments.
In this library the segments may be accessed using these separate,
bidirectional view types which reference the underlying URL:
// [table Segments Types [
// [Type]
// [Accessor]
// [Description]
// ][
// [__segments_view__]
// [[link url.ref.boost__urls__url_view_base.segments `segments`]]
// [
// A read-only range of decoded segments.
// ]
// ][
// [__segments_ref__]
// [[link url.ref.boost__urls__url_base.segments `segments`]]
// [
// A modifiable range of decoded segments.
// ]
// ][
// [__segments_encoded_view__]
// [[link url.ref.boost__urls__url_view_base.encoded_segments `encoded_segments`]]
// [
// A read-only range of segments.
// ]
// ][
// [__segments_encoded_ref__]
// [[link url.ref.boost__urls__url_base.encoded_segments `encoded_segments`]]
// [
// A modifiable range of segments.
// ]
// ]]
[cols="a,a,a"]
|===
// Headers
|Type|Accessor|Description
// Row 1, Column 1
|`segments_view`
// Row 1, Column 2
|`segments`
// Row 1, Column 3
|A read-only range of decoded segments.
// Row 2, Column 1
|`segments_ref`
// Row 2, Column 2
|`segments`
// Row 2, Column 3
|A modifiable range of decoded segments.
// Row 3, Column 1
|`segments_encoded_view`
// Row 3, Column 2
|`encoded_segments`
// Row 3, Column 3
|A read-only range of segments.
// Row 4, Column 1
|`segments_encoded_ref`
// Row 4, Column 2
|`encoded_segments`
// Row 4, Column 3
|A modifiable range of segments.
|===
First we observe these invariants about paths and segments:
@@ -73,9 +79,17 @@ we define this function `segs` which returns a list of
strings corresponding to the elements in a container of segments:
// code_container_4_1
[source,cpp]
----
// code_container_4_1
auto segs( core::string_view s ) -> std::list< std::string >
{
url_view u( s );
std::list< std::string > seq;
for( auto seg : u.encoded_segments() )
seq.push_back( seg.decode() );
return seq;
}
----
@@ -83,59 +97,97 @@ In this table we show the result of invoking `segs` with
different paths. This demonstrates how the library achieves
the invariants described above for various interesting cases:
// [table Segments [
// [s]
// [`segs( s )`]
// [absolute]
// ][
// [`""`]
// [`{ }`]
// []
// ][
// [`"/"`]
// [`{ }`]
// [yes]
// ][
// [`"./"`]
// [`{ "" }`]
// []
// ][
// [`"usr"`]
// [`{ "usr" }`]
// []
// ][
// [`"./usr"`]
// [`{ "usr" }`]
// []
// ][
// [`"/index.htm"`]
// [`{ "index.htm" }`]
// [yes]
// ][
// [`"/images/cat-pic.gif"`]
// [`{ "images", "cat-pic.gif" }`]
// [yes]
// ][
// [`"images/cat-pic.gif"`]
// [`{ "images", "cat-pic.gif" }`]
// []
// ][
// [`"/fast//query"`]
// [`{ "fast", "", "query" }`]
// [yes]
// ][
// [`"fast//"`]
// [`{ "fast", "", "" }`]
// []
// ][
// [`"/./"`]
// [`{ "" }`]
// [yes]
// ][
// [`".//"`]
// [`{ "", "" }`]
// []
// ]]
[cols="a,a,a"]
|===
// Headers
|s|`segs( s )`|absolute
// Row 1, Column 1
|`""`
// Row 1, Column 2
|`{ }`
// Row 1, Column 3
|
// Row 2, Column 1
|`"/"`
// Row 2, Column 2
|`{ }`
// Row 2, Column 3
|yes
// Row 3, Column 1
|`"./"`
// Row 3, Column 2
|`{ "" }`
// Row 3, Column 3
|
// Row 4, Column 1
|`"usr"`
// Row 4, Column 2
|`{ "usr" }`
// Row 4, Column 3
|
// Row 5, Column 1
|`"./usr"`
// Row 5, Column 2
|`{ "usr" }`
// Row 5, Column 3
|
// Row 6, Column 1
|`"/index.htm"`
// Row 6, Column 2
|`{ "index.htm" }`
// Row 6, Column 3
|yes
// Row 7, Column 1
|`"/images/cat-pic.gif"`
// Row 7, Column 2
|`{ "images", "cat-pic.gif" }`
// Row 7, Column 3
|yes
// Row 8, Column 1
|`"images/cat-pic.gif"`
// Row 8, Column 2
|`{ "images", "cat-pic.gif" }`
// Row 8, Column 3
|
// Row 9, Column 1
|`"/fast//query"`
// Row 9, Column 2
|`{ "fast", "", "query" }`
// Row 9, Column 3
|yes
// Row 10, Column 1
|`"fast//"`
// Row 10, Column 2
|`{ "fast", "", "" }`
// Row 10, Column 3
|
// Row 11, Column 1
|`"/./"`
// Row 11, Column 2
|`{ "" }`
// Row 11, Column 3
|yes
// Row 12, Column 1
|`".//"`
// Row 12, Column 2
|`{ "", "" }`
// Row 12, Column 3
|
|===
This implies that two paths may map to the same sequence of
segments . In the paths `"usr"` and `"./usr"`, the `"./"`
@@ -212,59 +264,97 @@ various modifications to a URL containing a path:
// set_encoded_path()
// edit_segments()
// [table Path Operations [
// [URL]
// [Operation]
// [Result]
// ][
// [`"info:kyle:xy"`]
// [`remove_scheme()`]
// [`"kyle%3Axy"`]
// ][
// [`"kyle%3Axy"`]
// [`set_scheme( "gopher" )`]
// [`"gopher:kyle:xy"`]
// ][
// [`"http://www.example.com//kyle:xy"`]
// [`remove_authority()`]
// [`"http:/.//kyle:xy"`]
// ][
// [`"//www.example.com//kyle:xy"`]
// [`remove_authority()`]
// [`"/.//kyle:xy"`]
// ][
// [`"http://www.example.com//kyle:xy"`]
// [`remove_origin()`]
// [`"/.//kyle:xy"`]
// ][
// [`"info:kyle:xy"`]
// [`remove_origin()`]
// [`"kyle%3Axy"`]
// ][
// [`"/kyle:xy"`]
// [`set_path_absolute( false )`]
// [`"kyle%3Axy"`]
// ][
// [`"kyle%3Axy"`]
// [`set_path_absolute( true )`]
// [`"/kyle:xy"`]
// ][
// [`""`]
// [`set_path( "kyle:xy" )`]
// [`"kyle%3Axy"`]
// ][
// [`""`]
// [`set_path( "//foo/fighters.txt" )`]
// [`"/.//foo/fighters.txt"`]
// ][
// [`"my%3Asharona/billa%3Abong"`]
// [`normalize()`]
// [`"my%3Asharona/billa:bong"`]
// ][
// [`"./my:sharona"`]
// [`normalize()`]
// [`"my%3Asharona"`]
// ]]
[cols="a,a,a"]
|===
// Headers
|URL|Operation|Result
// Row 1, Column 1
|`"info:kyle:xy"`
// Row 1, Column 2
|`remove_scheme()`
// Row 1, Column 3
|`"kyle%3Axy"`
// Row 2, Column 1
|`"kyle%3Axy"`
// Row 2, Column 2
|`set_scheme( "gopher" )`
// Row 2, Column 3
|`"gopher:kyle:xy"`
// Row 3, Column 1
|`"http://www.example.com//kyle:xy"`
// Row 3, Column 2
|`remove_authority()`
// Row 3, Column 3
|`"http:/.//kyle:xy"`
// Row 4, Column 1
|`"//www.example.com//kyle:xy"`
// Row 4, Column 2
|`remove_authority()`
// Row 4, Column 3
|`"/.//kyle:xy"`
// Row 5, Column 1
|`"http://www.example.com//kyle:xy"`
// Row 5, Column 2
|`remove_origin()`
// Row 5, Column 3
|`"/.//kyle:xy"`
// Row 6, Column 1
|`"info:kyle:xy"`
// Row 6, Column 2
|`remove_origin()`
// Row 6, Column 3
|`"kyle%3Axy"`
// Row 7, Column 1
|`"/kyle:xy"`
// Row 7, Column 2
|`set_path_absolute( false )`
// Row 7, Column 3
|`"kyle%3Axy"`
// Row 8, Column 1
|`"kyle%3Axy"`
// Row 8, Column 2
|`set_path_absolute( true )`
// Row 8, Column 3
|`"/kyle:xy"`
// Row 9, Column 1
|`""`
// Row 9, Column 2
|`set_path( "kyle:xy" )`
// Row 9, Column 3
|`"kyle%3Axy"`
// Row 10, Column 1
|`""`
// Row 10, Column 2
|`set_path( "//foo/fighters.txt" )`
// Row 10, Column 3
|`"/.//foo/fighters.txt"`
// Row 11, Column 1
|`"my%3Asharona/billa%3Abong"`
// Row 11, Column 2
|`normalize()`
// Row 11, Column 3
|`"my%3Asharona/billa:bong"`
// Row 12, Column 1
|`"./my:sharona"`
// Row 12, Column 2
|`normalize()`
// Row 12, Column 3
|`"my%3Asharona"`
|===
For the full set of containers and functions for operating
on paths and segments, please consult the reference.

View File

@@ -8,10 +8,10 @@
//
== String Token
= String Token
Functions which perform percent-decoding return values using
`std::string` when called without special arguments. This is
https://en.cppreference.com/w/cpp/string/basic_string[`std::string`,window=blank_] when called without special arguments. This is
the best default for ergonomics, and a good enough default for
performance considering that many decoded strings fit in
the small buffer available to most standard implementations.
@@ -23,7 +23,7 @@ algorithms acquire and store data in strings, for example:
* Appending to existing strings
The library provides a special customization mechanism called
`StringToken` to control how algorithms which require an output
__StringToken__ to control how algorithms which require an output
buffer acquire their storage. The signature

View File

@@ -4,112 +4,20 @@
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Official repository: https://github.com/cppalliance/json
Official repository: https://github.com/cppalliance/url
]
[/-----------------------------------------------------------------------------]
[section Examples]
[section QR Code]
A QR code is a machine-readable two-dimensional barcode. They might contain data
for a identifier or a URL to a website.
This example shows how to construct and modify URLs to consume a third party API to
generate QR Codes.
[example_qrcode]
[endsect]
[section Finicky]
This example shows how to classify URLs according to a set of rules. It is
inspired by [@https://github.com/johnste/finicky Finicky] application.
The URLs are classified and redirected to a browser according to their
category. See the example `config.json` file.
[example_finicky]
[endsect]
[section mailto URLs]
`mailto` is a URL scheme for email addresses. `mailto` URL are used on websites
to allow users to send an email to a specific address directly from an HTML document.
This example parses a mailto URL into a new view type and prints its components to
standard output.
[example_mailto]
[endsect]
[section Magnet Link]
`magnet` is a URL scheme for identifying files by their content. These files are
usually identified by cryptographic hash value.
Magnet links are useful in peer-to-peer file sharing networks because they allow
resources to be referred to without the need for a continuously available host..
This example parses a magnet link into a new view type and prints its components to
standard output.
[example_magnet]
[endsect]
[section File Router]
This example defines a router that associates URL paths to a directory in the filesystem. If
the specified route matches and the file exists, the example prints its contents to standard output.
[example_file_router]
[endsect]
[section Router]
This example defines a router for URL paths. If the specified route matches one of the existing
routes, the example executes the underlying callback function.
[example_router]
[endsect]
[section Sanitizing URLs]
This example parses a non-strict or invalid URL
into path components according to its delimiters.
This pattern can be adapted to the requirements of other
applications.
Once the non-strict components are determined, a new URL is
created and its parts are set with the `set_encoded_X`
functions, which will encode any invalid chars accordingly.
This sort of transformation is useful in applications that are
extremely loose in what kinds of URLs they accept, such as
browsers. The sanitized URL can later be used for machine-to-machine
communication.
Using non-strict URLs directly is a security concern in
machine-to-machine communication, is ambiguous, and also
involve an extra cost for the transformations.
Different transformations are required by different applications to
construct a valid URL appropriate for machine-to-machine communication.
For instance, if an invalid relative reference includes something that
looks like a host in the first path segment, browsers usually interpret
that as the host with an implicit "https" scheme. Other applications
also have other implicit schemes.
The example also identifies whether the input url is already valid.
It includes diagnostics that can be used to help the user determine
if a URL is invalid and why it's invalid.
Once all transformations are applied, the result is a URL
appropriate for machine-to-machine communication.
[example_sanitize_url]
[endsect]
[include 6.1.qrcode.qbk]
[include 6.2.finicky.qbk]
[include 6.3.mailto.qbk]
[include 6.4.magnet-link.qbk]
[include 6.5.file-router.qbk]
[include 6.6.router.qbk]
[include 6.7.sanitize.qbk]
[endsect]

21
doc/qbk/6.1.qrcode.qbk Normal file
View File

@@ -0,0 +1,21 @@
[/
Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com)
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Official repository: https://github.com/cppalliance/url
]
[/-----------------------------------------------------------------------------]
[section QR Code]
A QR code is a machine-readable two-dimensional barcode. They might contain data
for a identifier or a URL to a website.
This example shows how to construct and modify URLs to consume a third party API to
generate QR Codes.
[example_qrcode]
[endsect]

21
doc/qbk/6.2.finicky.qbk Normal file
View File

@@ -0,0 +1,21 @@
[/
Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com)
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Official repository: https://github.com/cppalliance/url
]
[/-----------------------------------------------------------------------------]
[section Finicky]
This example shows how to classify URLs according to a set of rules. It is
inspired by [@https://github.com/johnste/finicky Finicky] application.
The URLs are classified and redirected to a browser according to their
category. See the example `config.json` file.
[example_finicky]
[endsect]

21
doc/qbk/6.3.mailto.qbk Normal file
View File

@@ -0,0 +1,21 @@
[/
Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com)
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Official repository: https://github.com/cppalliance/url
]
[/-----------------------------------------------------------------------------]
[section mailto URLs]
`mailto` is a URL scheme for email addresses. `mailto` URL are used on websites
to allow users to send an email to a specific address directly from an HTML document.
This example parses a mailto URL into a new view type and prints its components to
standard output.
[example_mailto]
[endsect]

View File

@@ -0,0 +1,24 @@
[/
Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com)
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Official repository: https://github.com/cppalliance/url
]
[/-----------------------------------------------------------------------------]
[section Magnet Link]
`magnet` is a URL scheme for identifying files by their content. These files are
usually identified by cryptographic hash value.
Magnet links are useful in peer-to-peer file sharing networks because they allow
resources to be referred to without the need for a continuously available host..
This example parses a magnet link into a new view type and prints its components to
standard output.
[example_magnet]
[endsect]

View File

@@ -0,0 +1,19 @@
[/
Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com)
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Official repository: https://github.com/cppalliance/url
]
[/-----------------------------------------------------------------------------]
[section File Router]
This example defines a router that associates URL paths to a directory in the filesystem. If
the specified route matches and the file exists, the example prints its contents to standard output.
[example_file_router]
[endsect]

19
doc/qbk/6.6.router.qbk Normal file
View File

@@ -0,0 +1,19 @@
[/
Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com)
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Official repository: https://github.com/cppalliance/url
]
[/-----------------------------------------------------------------------------]
[section Router]
This example defines a router for URL paths. If the specified route matches one of the existing
routes, the example executes the underlying callback function.
[example_router]
[endsect]

47
doc/qbk/6.7.sanitize.qbk Normal file
View File

@@ -0,0 +1,47 @@
[/
Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com)
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Official repository: https://github.com/cppalliance/url
]
[/-----------------------------------------------------------------------------]
[section Sanitizing URLs]
This example parses a non-strict or invalid URL
into path components according to its delimiters.
This pattern can be adapted to the requirements of other
applications.
Once the non-strict components are determined, a new URL is
created and its parts are set with the `set_encoded_X`
functions, which will encode any invalid chars accordingly.
This sort of transformation is useful in applications that are
extremely loose in what kinds of URLs they accept, such as
browsers. The sanitized URL can later be used for machine-to-machine
communication.
Using non-strict URLs directly is a security concern in
machine-to-machine communication, is ambiguous, and also
involve an extra cost for the transformations.
Different transformations are required by different applications to
construct a valid URL appropriate for machine-to-machine communication.
For instance, if an invalid relative reference includes something that
looks like a host in the first path segment, browsers usually interpret
that as the host with an implicit "https" scheme. Other applications
also have other implicit schemes.
The example also identifies whether the input url is already valid.
It includes diagnostics that can be used to help the user determine
if a URL is invalid and why it's invalid.
Once all transformations are applied, the result is a URL
appropriate for machine-to-machine communication.
[example_sanitize_url]
[endsect]