Home | History | Annotate | Download | only in src
      1 /* Based on nsURLParsers.cc from Mozilla
      2  * -------------------------------------
      3  * The contents of this file are subject to the Mozilla Public License Version
      4  * 1.1 (the "License"); you may not use this file except in compliance with
      5  * the License. You may obtain a copy of the License at
      6  * http://www.mozilla.org/MPL/
      7  *
      8  * Software distributed under the License is distributed on an "AS IS" basis,
      9  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
     10  * for the specific language governing rights and limitations under the
     11  * License.
     12  *
     13  * The Original Code is mozilla.org code.
     14  *
     15  * The Initial Developer of the Original Code is
     16  * Netscape Communications Corporation.
     17  * Portions created by the Initial Developer are Copyright (C) 1998
     18  * the Initial Developer. All Rights Reserved.
     19  *
     20  * Contributor(s):
     21  *   Darin Fisher (original author)
     22  *
     23  * Alternatively, the contents of this file may be used under the terms of
     24  * either the GNU General Public License Version 2 or later (the "GPL"), or
     25  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
     26  * in which case the provisions of the GPL or the LGPL are applicable instead
     27  * of those above. If you wish to allow use of your version of this file only
     28  * under the terms of either the GPL or the LGPL, and not to allow others to
     29  * use your version of this file under the terms of the MPL, indicate your
     30  * decision by deleting the provisions above and replace them with the notice
     31  * and other provisions required by the GPL or the LGPL. If you do not delete
     32  * the provisions above, a recipient may use your version of this file under
     33  * the terms of any one of the MPL, the GPL or the LGPL.
     34  *
     35  * ***** END LICENSE BLOCK ***** */
     36 
     37 #include "googleurl/src/url_parse.h"
     38 
     39 #include <stdlib.h>
     40 
     41 #include "base/logging.h"
     42 #include "googleurl/src/url_parse_internal.h"
     43 
     44 namespace url_parse {
     45 
     46 namespace {
     47 
     48 // Returns true if the given character is a valid digit to use in a port.
     49 inline bool IsPortDigit(char16 ch) {
     50   return ch >= '0' && ch <= '9';
     51 }
     52 
     53 // Returns the offset of the next authority terminator in the input starting
     54 // from start_offset. If no terminator is found, the return value will be equal
     55 // to spec_len.
     56 template<typename CHAR>
     57 int FindNextAuthorityTerminator(const CHAR* spec,
     58                                 int start_offset,
     59                                 int spec_len) {
     60   for (int i = start_offset; i < spec_len; i++) {
     61     if (IsAuthorityTerminator(spec[i]))
     62       return i;
     63   }
     64   return spec_len;  // Not found.
     65 }
     66 
     67 template<typename CHAR>
     68 void ParseUserInfo(const CHAR* spec,
     69                    const Component& user,
     70                    Component* username,
     71                    Component* password) {
     72   // Find the first colon in the user section, which separates the username and
     73   // password.
     74   int colon_offset = 0;
     75   while (colon_offset < user.len && spec[user.begin + colon_offset] != ':')
     76     colon_offset++;
     77 
     78   if (colon_offset < user.len) {
     79     // Found separator: <username>:<password>
     80     *username = Component(user.begin, colon_offset);
     81     *password = MakeRange(user.begin + colon_offset + 1,
     82                           user.begin + user.len);
     83   } else {
     84     // No separator, treat everything as the username
     85     *username = user;
     86     *password = Component();
     87   }
     88 }
     89 
     90 template<typename CHAR>
     91 void ParseServerInfo(const CHAR* spec,
     92                      const Component& serverinfo,
     93                      Component* hostname,
     94                      Component* port_num) {
     95   if (serverinfo.len == 0) {
     96     // No server info, host name is empty.
     97     hostname->reset();
     98     port_num->reset();
     99     return;
    100   }
    101 
    102   // If the host starts with a left-bracket, assume the entire host is an
    103   // IPv6 literal.  Otherwise, assume none of the host is an IPv6 literal.
    104   // This assumption will be overridden if we find a right-bracket.
    105   //
    106   // Our IPv6 address canonicalization code requires both brackets to exist,
    107   // but the ability to locate an incomplete address can still be useful.
    108   int ipv6_terminator = spec[serverinfo.begin] == '[' ? serverinfo.end() : -1;
    109   int colon = -1;
    110 
    111   // Find the last right-bracket, and the last colon.
    112   for (int i = serverinfo.begin; i < serverinfo.end(); i++) {
    113     switch (spec[i]) {
    114       case ']':
    115         ipv6_terminator = i;
    116         break;
    117       case ':':
    118         colon = i;
    119         break;
    120     }
    121   }
    122 
    123   if (colon > ipv6_terminator) {
    124     // Found a port number: <hostname>:<port>
    125     *hostname = MakeRange(serverinfo.begin, colon);
    126     if (hostname->len == 0)
    127       hostname->reset();
    128     *port_num = MakeRange(colon + 1, serverinfo.end());
    129   } else {
    130     // No port: <hostname>
    131     *hostname = serverinfo;
    132     port_num->reset();
    133   }
    134 }
    135 
    136 // Given an already-identified auth section, breaks it into its consituent
    137 // parts. The port number will be parsed and the resulting integer will be
    138 // filled into the given *port variable, or -1 if there is no port number or it
    139 // is invalid.
    140 template<typename CHAR>
    141 void DoParseAuthority(const CHAR* spec,
    142                       const Component& auth,
    143                       Component* username,
    144                       Component* password,
    145                       Component* hostname,
    146                       Component* port_num) {
    147   DCHECK(auth.is_valid()) << "We should always get an authority";
    148   if (auth.len == 0) {
    149     username->reset();
    150     password->reset();
    151     hostname->reset();
    152     port_num->reset();
    153     return;
    154   }
    155 
    156   // Search backwards for @, which is the separator between the user info and
    157   // the server info.
    158   int i = auth.begin + auth.len - 1;
    159   while (i > auth.begin && spec[i] != '@')
    160     i--;
    161 
    162   if (spec[i] == '@') {
    163     // Found user info: <user-info>@<server-info>
    164     ParseUserInfo(spec, Component(auth.begin, i - auth.begin),
    165                   username, password);
    166     ParseServerInfo(spec, MakeRange(i + 1, auth.begin + auth.len),
    167                     hostname, port_num);
    168   } else {
    169     // No user info, everything is server info.
    170     username->reset();
    171     password->reset();
    172     ParseServerInfo(spec, auth, hostname, port_num);
    173   }
    174 }
    175 
    176 template<typename CHAR>
    177 void ParsePath(const CHAR* spec,
    178                const Component& path,
    179                Component* filepath,
    180                Component* query,
    181                Component* ref) {
    182   // path = [/]<segment1>/<segment2>/<...>/<segmentN>;<param>?<query>#<ref>
    183 
    184   // Special case when there is no path.
    185   if (path.len == -1) {
    186     filepath->reset();
    187     query->reset();
    188     ref->reset();
    189     return;
    190   }
    191   DCHECK(path.len > 0) << "We should never have 0 length paths";
    192 
    193   // Search for first occurrence of either ? or #.
    194   int path_end = path.begin + path.len;
    195 
    196   int query_separator = -1;  // Index of the '?'
    197   int ref_separator = -1;    // Index of the '#'
    198   for (int i = path.begin; i < path_end; i++) {
    199     switch (spec[i]) {
    200       case '?':
    201         // Only match the query string if it precedes the reference fragment
    202         // and when we haven't found one already.
    203         if (ref_separator < 0 && query_separator < 0)
    204           query_separator = i;
    205         break;
    206       case '#':
    207         // Record the first # sign only.
    208         if (ref_separator < 0)
    209           ref_separator = i;
    210         break;
    211     }
    212   }
    213 
    214   // Markers pointing to the character after each of these corresponding
    215   // components. The code below words from the end back to the beginning,
    216   // and will update these indices as it finds components that exist.
    217   int file_end, query_end;
    218 
    219   // Ref fragment: from the # to the end of the path.
    220   if (ref_separator >= 0) {
    221     file_end = query_end = ref_separator;
    222     *ref = MakeRange(ref_separator + 1, path_end);
    223   } else {
    224     file_end = query_end = path_end;
    225     ref->reset();
    226   }
    227 
    228   // Query fragment: everything from the ? to the next boundary (either the end
    229   // of the path or the ref fragment).
    230   if (query_separator >= 0) {
    231     file_end = query_separator;
    232     *query = MakeRange(query_separator + 1, query_end);
    233   } else {
    234     query->reset();
    235   }
    236 
    237   // File path: treat an empty file path as no file path.
    238   if (file_end != path.begin)
    239     *filepath = MakeRange(path.begin, file_end);
    240   else
    241     filepath->reset();
    242 }
    243 
    244 template<typename CHAR>
    245 bool DoExtractScheme(const CHAR* url,
    246                      int url_len,
    247                      Component* scheme) {
    248   // Skip leading whitespace and control characters.
    249   int begin = 0;
    250   while (begin < url_len && ShouldTrimFromURL(url[begin]))
    251     begin++;
    252   if (begin == url_len)
    253     return false;  // Input is empty or all whitespace.
    254 
    255   // Find the first colon character.
    256   for (int i = begin; i < url_len; i++) {
    257     if (url[i] == ':') {
    258       *scheme = MakeRange(begin, i);
    259       return true;
    260     }
    261   }
    262   return false;  // No colon found: no scheme
    263 }
    264 
    265 // Fills in all members of the Parsed structure except for the scheme.
    266 //
    267 // |spec| is the full spec being parsed, of length |spec_len|.
    268 // |after_scheme| is the character immediately following the scheme (after the
    269 //   colon) where we'll begin parsing.
    270 //
    271 // Compatability data points. I list "host", "path" extracted:
    272 // Input                IE6             Firefox                Us
    273 // -----                --------------  --------------         --------------
    274 // http://foo.com/      "foo.com", "/"  "foo.com", "/"         "foo.com", "/"
    275 // http:foo.com/        "foo.com", "/"  "foo.com", "/"         "foo.com", "/"
    276 // http:/foo.com/       fail(*)         "foo.com", "/"         "foo.com", "/"
    277 // http:\foo.com/       fail(*)         "\foo.com", "/"(fail)  "foo.com", "/"
    278 // http:////foo.com/    "foo.com", "/"  "foo.com", "/"         "foo.com", "/"
    279 //
    280 // (*) Interestingly, although IE fails to load these URLs, its history
    281 // canonicalizer handles them, meaning if you've been to the corresponding
    282 // "http://foo.com/" link, it will be colored.
    283 template <typename CHAR>
    284 void DoParseAfterScheme(const CHAR* spec,
    285                         int spec_len,
    286                         int after_scheme,
    287                         Parsed* parsed) {
    288   int num_slashes = CountConsecutiveSlashes(spec, after_scheme, spec_len);
    289   int after_slashes = after_scheme + num_slashes;
    290 
    291   // First split into two main parts, the authority (username, password, host,
    292   // and port) and the full path (path, query, and reference).
    293   Component authority;
    294   Component full_path;
    295 
    296   // Found "//<some data>", looks like an authority section. Treat everything
    297   // from there to the next slash (or end of spec) to be the authority. Note
    298   // that we ignore the number of slashes and treat it as the authority.
    299   int end_auth = FindNextAuthorityTerminator(spec, after_slashes, spec_len);
    300   authority = Component(after_slashes, end_auth - after_slashes);
    301 
    302   if (end_auth == spec_len)  // No beginning of path found.
    303     full_path = Component();
    304   else  // Everything starting from the slash to the end is the path.
    305     full_path = Component(end_auth, spec_len - end_auth);
    306 
    307   // Now parse those two sub-parts.
    308   DoParseAuthority(spec, authority, &parsed->username, &parsed->password,
    309                    &parsed->host, &parsed->port);
    310   ParsePath(spec, full_path, &parsed->path, &parsed->query, &parsed->ref);
    311 }
    312 
    313 // The main parsing function for standard URLs. Standard URLs have a scheme,
    314 // host, path, etc.
    315 template<typename CHAR>
    316 void DoParseStandardURL(const CHAR* spec, int spec_len, Parsed* parsed) {
    317   DCHECK(spec_len >= 0);
    318 
    319   // Strip leading & trailing spaces and control characters.
    320   int begin = 0;
    321   TrimURL(spec, &begin, &spec_len);
    322 
    323   int after_scheme;
    324   if (DoExtractScheme(spec, spec_len, &parsed->scheme)) {
    325     after_scheme = parsed->scheme.end() + 1;  // Skip past the colon.
    326   } else {
    327     // Say there's no scheme when there is no colon. We could also say that
    328     // everything is the scheme. Both would produce an invalid URL, but this way
    329     // seems less wrong in more cases.
    330     parsed->scheme.reset();
    331     after_scheme = begin;
    332   }
    333   DoParseAfterScheme(spec, spec_len, after_scheme, parsed);
    334 }
    335 
    336 // Initializes a path URL which is merely a scheme followed by a path. Examples
    337 // include "about:foo" and "javascript:alert('bar');"
    338 template<typename CHAR>
    339 void DoParsePathURL(const CHAR* spec, int spec_len, Parsed* parsed) {
    340   // Get the non-path and non-scheme parts of the URL out of the way, we never
    341   // use them.
    342   parsed->username.reset();
    343   parsed->password.reset();
    344   parsed->host.reset();
    345   parsed->port.reset();
    346   parsed->query.reset();
    347   parsed->ref.reset();
    348 
    349   // Strip leading & trailing spaces and control characters.
    350   int begin = 0;
    351   TrimURL(spec, &begin, &spec_len);
    352 
    353   // Handle empty specs or ones that contain only whitespace or control chars.
    354   if (begin == spec_len) {
    355     parsed->scheme.reset();
    356     parsed->path.reset();
    357     return;
    358   }
    359 
    360   // Extract the scheme, with the path being everything following. We also
    361   // handle the case where there is no scheme.
    362   if (ExtractScheme(&spec[begin], spec_len - begin, &parsed->scheme)) {
    363     // Offset the results since we gave ExtractScheme a substring.
    364     parsed->scheme.begin += begin;
    365 
    366     // For compatability with the standard URL parser, we treat no path as
    367     // -1, rather than having a length of 0 (we normally wouldn't care so
    368     // much for these non-standard URLs).
    369     if (parsed->scheme.end() == spec_len - 1)
    370       parsed->path.reset();
    371     else
    372       parsed->path = MakeRange(parsed->scheme.end() + 1, spec_len);
    373   } else {
    374     // No scheme found, just path.
    375     parsed->scheme.reset();
    376     parsed->path = MakeRange(begin, spec_len);
    377   }
    378 }
    379 
    380 template<typename CHAR>
    381 void DoParseMailtoURL(const CHAR* spec, int spec_len, Parsed* parsed) {
    382   DCHECK(spec_len >= 0);
    383 
    384   // Get the non-path and non-scheme parts of the URL out of the way, we never
    385   // use them.
    386   parsed->username.reset();
    387   parsed->password.reset();
    388   parsed->host.reset();
    389   parsed->port.reset();
    390   parsed->ref.reset();
    391   parsed->query.reset();  // May use this; reset for convenience.
    392 
    393   // Strip leading & trailing spaces and control characters.
    394   int begin = 0;
    395   TrimURL(spec, &begin, &spec_len);
    396 
    397   // Handle empty specs or ones that contain only whitespace or control chars.
    398   if (begin == spec_len) {
    399     parsed->scheme.reset();
    400     parsed->path.reset();
    401     return;
    402   }
    403 
    404   int path_begin = -1;
    405   int path_end = -1;
    406 
    407   // Extract the scheme, with the path being everything following. We also
    408   // handle the case where there is no scheme.
    409   if (ExtractScheme(&spec[begin], spec_len - begin, &parsed->scheme)) {
    410     // Offset the results since we gave ExtractScheme a substring.
    411     parsed->scheme.begin += begin;
    412 
    413     if (parsed->scheme.end() != spec_len - 1) {
    414       path_begin = parsed->scheme.end() + 1;
    415       path_end = spec_len;
    416     }
    417   } else {
    418     // No scheme found, just path.
    419     parsed->scheme.reset();
    420     path_begin = begin;
    421     path_end = spec_len;
    422   }
    423 
    424   // Split [path_begin, path_end) into a path + query.
    425   for (int i = path_begin; i < path_end; ++i) {
    426     if (spec[i] == '?') {
    427       parsed->query = MakeRange(i + 1, path_end);
    428       path_end = i;
    429       break;
    430     }
    431   }
    432 
    433   // For compatability with the standard URL parser, treat no path as
    434   // -1, rather than having a length of 0
    435   if (path_begin == path_end) {
    436     parsed->path.reset();
    437   } else {
    438     parsed->path = MakeRange(path_begin, path_end);
    439   }
    440 }
    441 
    442 // Converts a port number in a string to an integer. We'd like to just call
    443 // sscanf but our input is not NULL-terminated, which sscanf requires. Instead,
    444 // we copy the digits to a small stack buffer (since we know the maximum number
    445 // of digits in a valid port number) that we can NULL terminate.
    446 template<typename CHAR>
    447 int DoParsePort(const CHAR* spec, const Component& component) {
    448   // Easy success case when there is no port.
    449   const int kMaxDigits = 5;
    450   if (!component.is_nonempty())
    451     return PORT_UNSPECIFIED;
    452 
    453   // Skip over any leading 0s.
    454   Component digits_comp(component.end(), 0);
    455   for (int i = 0; i < component.len; i++) {
    456     if (spec[component.begin + i] != '0') {
    457       digits_comp = MakeRange(component.begin + i, component.end());
    458       break;
    459     }
    460   }
    461   if (digits_comp.len == 0)
    462     return 0;  // All digits were 0.
    463 
    464   // Verify we don't have too many digits (we'll be copying to our buffer so
    465   // we need to double-check).
    466   if (digits_comp.len > kMaxDigits)
    467     return PORT_INVALID;
    468 
    469   // Copy valid digits to the buffer.
    470   char digits[kMaxDigits + 1];  // +1 for null terminator
    471   for (int i = 0; i < digits_comp.len; i++) {
    472     CHAR ch = spec[digits_comp.begin + i];
    473     if (!IsPortDigit(ch)) {
    474       // Invalid port digit, fail.
    475       return PORT_INVALID;
    476     }
    477     digits[i] = static_cast<char>(ch);
    478   }
    479 
    480   // Null-terminate the string and convert to integer. Since we guarantee
    481   // only digits, atoi's lack of error handling is OK.
    482   digits[digits_comp.len] = 0;
    483   int port = atoi(digits);
    484   if (port > 65535)
    485     return PORT_INVALID;  // Out of range.
    486   return port;
    487 }
    488 
    489 template<typename CHAR>
    490 void DoExtractFileName(const CHAR* spec,
    491                        const Component& path,
    492                        Component* file_name) {
    493   // Handle empty paths: they have no file names.
    494   if (!path.is_nonempty()) {
    495     file_name->reset();
    496     return;
    497   }
    498 
    499   // Search backwards for a parameter, which is a normally unused field in a
    500   // URL delimited by a semicolon. We parse the parameter as part of the
    501   // path, but here, we don't want to count it. The last semicolon is the
    502   // parameter. The path should start with a slash, so we don't need to check
    503   // the first one.
    504   int file_end = path.end();
    505   for (int i = path.end() - 1; i > path.begin; i--) {
    506     if (spec[i] == ';') {
    507       file_end = i;
    508       break;
    509     }
    510   }
    511 
    512   // Now search backwards from the filename end to the previous slash
    513   // to find the beginning of the filename.
    514   for (int i = file_end - 1; i >= path.begin; i--) {
    515     if (IsURLSlash(spec[i])) {
    516       // File name is everything following this character to the end
    517       *file_name = MakeRange(i + 1, file_end);
    518       return;
    519     }
    520   }
    521 
    522   // No slash found, this means the input was degenerate (generally paths
    523   // will start with a slash). Let's call everything the file name.
    524   *file_name = MakeRange(path.begin, file_end);
    525   return;
    526 }
    527 
    528 template<typename CHAR>
    529 bool DoExtractQueryKeyValue(const CHAR* spec,
    530                             Component* query,
    531                             Component* key,
    532                             Component* value) {
    533   if (!query->is_nonempty())
    534     return false;
    535 
    536   int start = query->begin;
    537   int cur = start;
    538   int end = query->end();
    539 
    540   // We assume the beginning of the input is the beginning of the "key" and we
    541   // skip to the end of it.
    542   key->begin = cur;
    543   while (cur < end && spec[cur] != '&' && spec[cur] != '=')
    544     cur++;
    545   key->len = cur - key->begin;
    546 
    547   // Skip the separator after the key (if any).
    548   if (cur < end && spec[cur] == '=')
    549     cur++;
    550 
    551   // Find the value part.
    552   value->begin = cur;
    553   while (cur < end && spec[cur] != '&')
    554     cur++;
    555   value->len = cur - value->begin;
    556 
    557   // Finally skip the next separator if any
    558   if (cur < end && spec[cur] == '&')
    559     cur++;
    560 
    561   // Save the new query
    562   *query = url_parse::MakeRange(cur, end);
    563   return true;
    564 }
    565 
    566 }  // namespace
    567 
    568 Parsed::Parsed() {
    569 }
    570 
    571 int Parsed::Length() const {
    572   if (ref.is_valid())
    573     return ref.end();
    574   return CountCharactersBefore(REF, false);
    575 }
    576 
    577 int Parsed::CountCharactersBefore(ComponentType type,
    578                                   bool include_delimiter) const {
    579   if (type == SCHEME)
    580     return scheme.begin;
    581 
    582   // There will be some characters after the scheme like "://" and we don't
    583   // know how many. Search forwards for the next thing until we find one.
    584   int cur = 0;
    585   if (scheme.is_valid())
    586     cur = scheme.end() + 1;  // Advance over the ':' at the end of the scheme.
    587 
    588   if (username.is_valid()) {
    589     if (type <= USERNAME)
    590       return username.begin;
    591     cur = username.end() + 1;  // Advance over the '@' or ':' at the end.
    592   }
    593 
    594   if (password.is_valid()) {
    595     if (type <= PASSWORD)
    596       return password.begin;
    597     cur = password.end() + 1;  // Advance over the '@' at the end.
    598   }
    599 
    600   if (host.is_valid()) {
    601     if (type <= HOST)
    602       return host.begin;
    603     cur = host.end();
    604   }
    605 
    606   if (port.is_valid()) {
    607     if (type < PORT || (type == PORT && include_delimiter))
    608       return port.begin - 1;  // Back over delimiter.
    609     if (type == PORT)
    610       return port.begin;  // Don't want delimiter counted.
    611     cur = port.end();
    612   }
    613 
    614   if (path.is_valid()) {
    615     if (type <= PATH)
    616       return path.begin;
    617     cur = path.end();
    618   }
    619 
    620   if (query.is_valid()) {
    621     if (type < QUERY || (type == QUERY && include_delimiter))
    622       return query.begin - 1;  // Back over delimiter.
    623     if (type == QUERY)
    624       return query.begin;  // Don't want delimiter counted.
    625     cur = query.end();
    626   }
    627 
    628   if (ref.is_valid()) {
    629     if (type == REF && !include_delimiter)
    630       return ref.begin;  // Back over delimiter.
    631 
    632     // When there is a ref and we get here, the component we wanted was before
    633     // this and not found, so we always know the beginning of the ref is right.
    634     return ref.begin - 1;  // Don't want delimiter counted.
    635   }
    636 
    637   return cur;
    638 }
    639 
    640 bool ExtractScheme(const char* url, int url_len, Component* scheme) {
    641   return DoExtractScheme(url, url_len, scheme);
    642 }
    643 
    644 bool ExtractScheme(const char16* url, int url_len, Component* scheme) {
    645   return DoExtractScheme(url, url_len, scheme);
    646 }
    647 
    648 // This handles everything that may be an authority terminator, including
    649 // backslash. For special backslash handling see DoParseAfterScheme.
    650 bool IsAuthorityTerminator(char16 ch) {
    651   return IsURLSlash(ch) || ch == '?' || ch == '#';
    652 }
    653 
    654 void ExtractFileName(const char* url,
    655                      const Component& path,
    656                      Component* file_name) {
    657   DoExtractFileName(url, path, file_name);
    658 }
    659 
    660 void ExtractFileName(const char16* url,
    661                      const Component& path,
    662                      Component* file_name) {
    663   DoExtractFileName(url, path, file_name);
    664 }
    665 
    666 bool ExtractQueryKeyValue(const char* url,
    667                           Component* query,
    668                           Component* key,
    669                           Component* value) {
    670   return DoExtractQueryKeyValue(url, query, key, value);
    671 }
    672 
    673 bool ExtractQueryKeyValue(const char16* url,
    674                           Component* query,
    675                           Component* key,
    676                           Component* value) {
    677   return DoExtractQueryKeyValue(url, query, key, value);
    678 }
    679 
    680 void ParseAuthority(const char* spec,
    681                     const Component& auth,
    682                     Component* username,
    683                     Component* password,
    684                     Component* hostname,
    685                     Component* port_num) {
    686   DoParseAuthority(spec, auth, username, password, hostname, port_num);
    687 }
    688 
    689 void ParseAuthority(const char16* spec,
    690                     const Component& auth,
    691                     Component* username,
    692                     Component* password,
    693                     Component* hostname,
    694                     Component* port_num) {
    695   DoParseAuthority(spec, auth, username, password, hostname, port_num);
    696 }
    697 
    698 int ParsePort(const char* url, const Component& port) {
    699   return DoParsePort(url, port);
    700 }
    701 
    702 int ParsePort(const char16* url, const Component& port) {
    703   return DoParsePort(url, port);
    704 }
    705 
    706 void ParseStandardURL(const char* url, int url_len, Parsed* parsed) {
    707   DoParseStandardURL(url, url_len, parsed);
    708 }
    709 
    710 void ParseStandardURL(const char16* url, int url_len, Parsed* parsed) {
    711   DoParseStandardURL(url, url_len, parsed);
    712 }
    713 
    714 void ParsePathURL(const char* url, int url_len, Parsed* parsed) {
    715   DoParsePathURL(url, url_len, parsed);
    716 }
    717 
    718 void ParsePathURL(const char16* url, int url_len, Parsed* parsed) {
    719   DoParsePathURL(url, url_len, parsed);
    720 }
    721 
    722 void ParseMailtoURL(const char* url, int url_len, Parsed* parsed) {
    723   DoParseMailtoURL(url, url_len, parsed);
    724 }
    725 
    726 void ParseMailtoURL(const char16* url, int url_len, Parsed* parsed) {
    727   DoParseMailtoURL(url, url_len, parsed);
    728 }
    729 
    730 void ParsePathInternal(const char* spec,
    731                        const Component& path,
    732                        Component* filepath,
    733                        Component* query,
    734                        Component* ref) {
    735   ParsePath(spec, path, filepath, query, ref);
    736 }
    737 
    738 void ParsePathInternal(const char16* spec,
    739                        const Component& path,
    740                        Component* filepath,
    741                        Component* query,
    742                        Component* ref) {
    743   ParsePath(spec, path, filepath, query, ref);
    744 }
    745 
    746 void ParseAfterScheme(const char* spec,
    747                       int spec_len,
    748                       int after_scheme,
    749                       Parsed* parsed) {
    750   DoParseAfterScheme(spec, spec_len, after_scheme, parsed);
    751 }
    752 
    753 void ParseAfterScheme(const char16* spec,
    754                       int spec_len,
    755                       int after_scheme,
    756                       Parsed* parsed) {
    757   DoParseAfterScheme(spec, spec_len, after_scheme, parsed);
    758 }
    759 
    760 }  // namespace url_parse
    761