123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417 |
- // Copyright 2013 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- // Canonicalizers for random bits that aren't big enough for their own files.
- #include <string.h>
- #include "url/url_canon.h"
- #include "url/url_canon_internal.h"
- namespace url {
- namespace {
- // Returns true if the given character should be removed from the middle of a
- // URL.
- inline bool IsRemovableURLWhitespace(int ch) {
- return ch == '\r' || ch == '\n' || ch == '\t';
- }
- // Backend for RemoveURLWhitespace (see declaration in url_canon.h).
- // It sucks that we have to do this, since this takes about 13% of the total URL
- // canonicalization time.
- template <typename CHAR>
- const CHAR* DoRemoveURLWhitespace(const CHAR* input,
- int input_len,
- CanonOutputT<CHAR>* buffer,
- int* output_len,
- bool* potentially_dangling_markup) {
- // Fast verification that there's nothing that needs removal. This is the 99%
- // case, so we want it to be fast and don't care about impacting the speed
- // when we do find whitespace.
- int found_whitespace = false;
- for (int i = 0; i < input_len; i++) {
- if (!IsRemovableURLWhitespace(input[i]))
- continue;
- found_whitespace = true;
- break;
- }
- if (!found_whitespace) {
- // Didn't find any whitespace, we don't need to do anything. We can just
- // return the input as the output.
- *output_len = input_len;
- return input;
- }
- // Skip whitespace removal for `data:` URLs.
- //
- // TODO(mkwst): Ideally, this would use something like `base::StartsWith`, but
- // that turns out to be difficult to do correctly given this function's
- // character type templating.
- if (input_len > 5 && input[0] == 'd' && input[1] == 'a' && input[2] == 't' &&
- input[3] == 'a' && input[4] == ':') {
- *output_len = input_len;
- return input;
- }
- // Remove the whitespace into the new buffer and return it.
- for (int i = 0; i < input_len; i++) {
- if (!IsRemovableURLWhitespace(input[i])) {
- if (potentially_dangling_markup && input[i] == 0x3C)
- *potentially_dangling_markup = true;
- buffer->push_back(input[i]);
- }
- }
- *output_len = buffer->length();
- return buffer->data();
- }
- // Contains the canonical version of each possible input letter in the scheme
- // (basically, lower-cased). The corresponding entry will be 0 if the letter
- // is not allowed in a scheme.
- const char kSchemeCanonical[0x80] = {
- // 00-1f: all are invalid
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- // ' ' ! " # $ % & ' ( ) * + , - . /
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '+', 0, '-', '.', 0,
- // 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0 , 0 , 0 , 0 , 0 , 0 ,
- // @ A B C D E F G H I J K L M N O
- 0 , 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
- // P Q R S T U V W X Y Z [ \ ] ^ _
- 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0 , 0, 0 , 0,
- // ` a b c d e f g h i j k l m n o
- 0 , 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
- // p q r s t u v w x y z { | } ~
- 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0 , 0 , 0 , 0 , 0 };
- // This could be a table lookup as well by setting the high bit for each
- // valid character, but it's only called once per URL, and it makes the lookup
- // table easier to read not having extra stuff in it.
- inline bool IsSchemeFirstChar(unsigned char c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
- }
- template<typename CHAR, typename UCHAR>
- bool DoScheme(const CHAR* spec,
- const Component& scheme,
- CanonOutput* output,
- Component* out_scheme) {
- if (!scheme.is_nonempty()) {
- // Scheme is unspecified or empty, convert to empty by appending a colon.
- *out_scheme = Component(output->length(), 0);
- output->push_back(':');
- return false;
- }
- // The output scheme starts from the current position.
- out_scheme->begin = output->length();
- // Danger: it's important that this code does not strip any characters;
- // it only emits the canonical version (be it valid or escaped) for each
- // of the input characters. Stripping would put it out of sync with
- // FindAndCompareScheme, which could cause some security checks on
- // schemes to be incorrect.
- bool success = true;
- size_t begin = static_cast<size_t>(scheme.begin);
- size_t end = static_cast<size_t>(scheme.end());
- for (size_t i = begin; i < end; i++) {
- UCHAR ch = static_cast<UCHAR>(spec[i]);
- char replacement = 0;
- if (ch < 0x80) {
- if (i == begin) {
- // Need to do a special check for the first letter of the scheme.
- if (IsSchemeFirstChar(static_cast<unsigned char>(ch)))
- replacement = kSchemeCanonical[ch];
- } else {
- replacement = kSchemeCanonical[ch];
- }
- }
- if (replacement) {
- output->push_back(replacement);
- } else if (ch == '%') {
- // Canonicalizing the scheme multiple times should lead to the same
- // result. Since invalid characters will be escaped, we need to preserve
- // the percent to avoid multiple escaping. The scheme will be invalid.
- success = false;
- output->push_back('%');
- } else {
- // Invalid character, store it but mark this scheme as invalid.
- success = false;
- // This will escape the output and also handle encoding issues.
- // Ignore the return value since we already failed.
- AppendUTF8EscapedChar(spec, &i, end, output);
- }
- }
- // The output scheme ends with the the current position, before appending
- // the colon.
- out_scheme->len = output->length() - out_scheme->begin;
- output->push_back(':');
- return success;
- }
- // The username and password components reference ranges in the corresponding
- // *_spec strings. Typically, these specs will be the same (we're
- // canonicalizing a single source string), but may be different when
- // replacing components.
- template<typename CHAR, typename UCHAR>
- bool DoUserInfo(const CHAR* username_spec,
- const Component& username,
- const CHAR* password_spec,
- const Component& password,
- CanonOutput* output,
- Component* out_username,
- Component* out_password) {
- if (username.len <= 0 && password.len <= 0) {
- // Common case: no user info. We strip empty username/passwords.
- *out_username = Component();
- *out_password = Component();
- return true;
- }
- // Write the username.
- out_username->begin = output->length();
- if (username.len > 0) {
- // This will escape characters not valid for the username.
- AppendStringOfType(&username_spec[username.begin],
- static_cast<size_t>(username.len), CHAR_USERINFO,
- output);
- }
- out_username->len = output->length() - out_username->begin;
- // When there is a password, we need the separator. Note that we strip
- // empty but specified passwords.
- if (password.len > 0) {
- output->push_back(':');
- out_password->begin = output->length();
- AppendStringOfType(&password_spec[password.begin],
- static_cast<size_t>(password.len), CHAR_USERINFO,
- output);
- out_password->len = output->length() - out_password->begin;
- } else {
- *out_password = Component();
- }
- output->push_back('@');
- return true;
- }
- // Helper functions for converting port integers to strings.
- inline void WritePortInt(char* output, int output_len, int port) {
- _itoa_s(port, output, output_len, 10);
- }
- // This function will prepend the colon if there will be a port.
- template<typename CHAR, typename UCHAR>
- bool DoPort(const CHAR* spec,
- const Component& port,
- int default_port_for_scheme,
- CanonOutput* output,
- Component* out_port) {
- int port_num = ParsePort(spec, port);
- if (port_num == PORT_UNSPECIFIED || port_num == default_port_for_scheme) {
- *out_port = Component();
- return true; // Leave port empty.
- }
- if (port_num == PORT_INVALID) {
- // Invalid port: We'll copy the text from the input so the user can see
- // what the error was, and mark the URL as invalid by returning false.
- output->push_back(':');
- out_port->begin = output->length();
- AppendInvalidNarrowString(spec, static_cast<size_t>(port.begin),
- static_cast<size_t>(port.end()), output);
- out_port->len = output->length() - out_port->begin;
- return false;
- }
- // Convert port number back to an integer. Max port value is 5 digits, and
- // the Parsed::ExtractPort will have made sure the integer is in range.
- const int buf_size = 6;
- char buf[buf_size];
- WritePortInt(buf, buf_size, port_num);
- // Append the port number to the output, preceded by a colon.
- output->push_back(':');
- out_port->begin = output->length();
- for (int i = 0; i < buf_size && buf[i]; i++)
- output->push_back(buf[i]);
- out_port->len = output->length() - out_port->begin;
- return true;
- }
- // clang-format off
- // Percent-escape all characters from the fragment percent-encode set
- // https://url.spec.whatwg.org/#fragment-percent-encode-set
- const bool kShouldEscapeCharInFragment[0x80] = {
- // Control characters (0x00-0x1F)
- true, true, true, true, true, true, true, true,
- true, true, true, true, true, true, true, true,
- true, true, true, true, true, true, true, true,
- true, true, true, true, true, true, true, true,
- // ' ' ! " # $ % & '
- true, false, true, false, false, false, false, false,
- // ( ) * + , - . /
- false, false, false, false, false, false, false, false,
- // 0 1 2 3 4 5 6 7
- false, false, false, false, false, false, false, false,
- // 8 9 : ; < = > ?
- false, false, false, false, true, false, true, false,
- // @ A B C D E F G
- false, false, false, false, false, false, false, false,
- // H I J K L M N O
- false, false, false, false, false, false, false, false,
- // P Q R S T U V W
- false, false, false, false, false, false, false, false,
- // X Y Z [ \ ] ^ _
- false, false, false, false, false, false, false, false,
- // ` a b c d e f g
- true, false, false, false, false, false, false, false,
- // h i j k l m n o
- false, false, false, false, false, false, false, false,
- // p q r s t u v w
- false, false, false, false, false, false, false, false,
- // x y z { | } ~ DELETE
- false, false, false, false, false, false, false, true
- };
- // clang-format on
- template<typename CHAR, typename UCHAR>
- void DoCanonicalizeRef(const CHAR* spec,
- const Component& ref,
- CanonOutput* output,
- Component* out_ref) {
- if (!ref.is_valid()) {
- // Common case of no ref.
- *out_ref = Component();
- return;
- }
- // Append the ref separator. Note that we need to do this even when the ref
- // is empty but present.
- output->push_back('#');
- out_ref->begin = output->length();
- // Now iterate through all the characters, converting to UTF-8 and validating.
- size_t end = static_cast<size_t>(ref.end());
- for (size_t i = static_cast<size_t>(ref.begin); i < end; i++) {
- UCHAR current_char = static_cast<UCHAR>(spec[i]);
- if (current_char < 0x80) {
- if (kShouldEscapeCharInFragment[current_char])
- AppendEscapedChar(static_cast<unsigned char>(spec[i]), output);
- else
- output->push_back(static_cast<char>(spec[i]));
- } else {
- AppendUTF8EscapedChar(spec, &i, end, output);
- }
- }
- out_ref->len = output->length() - out_ref->begin;
- }
- } // namespace
- const char* RemoveURLWhitespace(const char* input,
- int input_len,
- CanonOutputT<char>* buffer,
- int* output_len,
- bool* potentially_dangling_markup) {
- return DoRemoveURLWhitespace(input, input_len, buffer, output_len,
- potentially_dangling_markup);
- }
- const char16_t* RemoveURLWhitespace(const char16_t* input,
- int input_len,
- CanonOutputT<char16_t>* buffer,
- int* output_len,
- bool* potentially_dangling_markup) {
- return DoRemoveURLWhitespace(input, input_len, buffer, output_len,
- potentially_dangling_markup);
- }
- char CanonicalSchemeChar(char16_t ch) {
- if (ch >= 0x80)
- return 0; // Non-ASCII is not supported by schemes.
- return kSchemeCanonical[ch];
- }
- bool CanonicalizeScheme(const char* spec,
- const Component& scheme,
- CanonOutput* output,
- Component* out_scheme) {
- return DoScheme<char, unsigned char>(spec, scheme, output, out_scheme);
- }
- bool CanonicalizeScheme(const char16_t* spec,
- const Component& scheme,
- CanonOutput* output,
- Component* out_scheme) {
- return DoScheme<char16_t, char16_t>(spec, scheme, output, out_scheme);
- }
- bool CanonicalizeUserInfo(const char* username_source,
- const Component& username,
- const char* password_source,
- const Component& password,
- CanonOutput* output,
- Component* out_username,
- Component* out_password) {
- return DoUserInfo<char, unsigned char>(
- username_source, username, password_source, password,
- output, out_username, out_password);
- }
- bool CanonicalizeUserInfo(const char16_t* username_source,
- const Component& username,
- const char16_t* password_source,
- const Component& password,
- CanonOutput* output,
- Component* out_username,
- Component* out_password) {
- return DoUserInfo<char16_t, char16_t>(username_source, username,
- password_source, password, output,
- out_username, out_password);
- }
- bool CanonicalizePort(const char* spec,
- const Component& port,
- int default_port_for_scheme,
- CanonOutput* output,
- Component* out_port) {
- return DoPort<char, unsigned char>(spec, port,
- default_port_for_scheme,
- output, out_port);
- }
- bool CanonicalizePort(const char16_t* spec,
- const Component& port,
- int default_port_for_scheme,
- CanonOutput* output,
- Component* out_port) {
- return DoPort<char16_t, char16_t>(spec, port, default_port_for_scheme, output,
- out_port);
- }
- void CanonicalizeRef(const char* spec,
- const Component& ref,
- CanonOutput* output,
- Component* out_ref) {
- DoCanonicalizeRef<char, unsigned char>(spec, ref, output, out_ref);
- }
- void CanonicalizeRef(const char16_t* spec,
- const Component& ref,
- CanonOutput* output,
- Component* out_ref) {
- DoCanonicalizeRef<char16_t, char16_t>(spec, ref, output, out_ref);
- }
- } // namespace url
|