All Downloads are FREE. Search and download functionalities are using the official Maven repository.

feign.template.UriUtils Maven / Gradle / Ivy

There is a newer version: 13.5
Show newest version
/**
 * Copyright 2012-2020 The Feign Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package feign.template;

import feign.Util;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class UriUtils {

  private static final Pattern PCT_ENCODED_PATTERN = Pattern.compile("%[0-9A-Fa-f][0-9A-Fa-f]");

  /**
   * Determines if the value is already pct-encoded.
   *
   * @param value to check.
   * @return {@literal true} if the value is already pct-encoded
   */
  public static boolean isEncoded(String value, Charset charset) {
    for (byte b : value.getBytes(charset)) {
      if (!isUnreserved((char) b) && b != '%') {
        /* break if there are any unreserved character */
        return false;
      }
    }
    return PCT_ENCODED_PATTERN.matcher(value).find();
  }

  /**
   * Uri Encode the value, using the default Charset. Already encoded values are skipped.
   *
   * @param value to encode.
   * @return the encoded value.
   */
  public static String encode(String value) {
    return encodeChunk(value, Util.UTF_8, false);
  }

  /**
   * Uri Encode the value. Already encoded values are skipped.
   *
   * @param value to encode.
   * @param charset to use.
   * @return the encoded value.
   */
  public static String encode(String value, Charset charset) {
    return encodeChunk(value, charset, false);
  }

  public static String encode(String value, boolean allowReservedCharacters) {
    return encodeInternal(value, Util.UTF_8, allowReservedCharacters);
  }

  public static String encode(String value, Charset charset, boolean allowReservedCharacters) {
    return encodeInternal(value, charset, allowReservedCharacters);
  }

  /**
   * Uri Decode the value.
   *
   * @param value to decode
   * @param charset to use.
   * @return the decoded value.
   */
  public static String decode(String value, Charset charset) {
    try {
      /* there is nothing special between uri and url decoding */
      return URLDecoder.decode(value, charset.name());
    } catch (UnsupportedEncodingException uee) {
      /* since the encoding is not supported, return the original value */
      return value;
    }
  }


  /**
   * Determines if the provided uri is an absolute uri.
   *
   * @param uri to evaluate.
   * @return true if the uri is absolute.
   */
  public static boolean isAbsolute(String uri) {
    return uri != null && !uri.isEmpty() && uri.startsWith("http");
  }


  /**
   * Encodes the value, preserving all reserved characters.. Values that are already pct-encoded are
   * ignored.
   *
   * @param value inspect.
   * @param charset to use.
   * @return a new String with the reserved characters preserved.
   */
  public static String encodeInternal(String value,
                                      Charset charset,
                                      boolean allowReservedCharacters) {
    /* value is encoded, we need to split it up and skip the parts that are already encoded */
    Matcher matcher = PCT_ENCODED_PATTERN.matcher(value);

    if (!matcher.find()) {
      return encodeChunk(value, charset, true);
    }

    int length = value.length();
    StringBuilder encoded = new StringBuilder(length + 8);
    int index = 0;
    do {
      /* split out the value before the encoded value */
      String before = value.substring(index, matcher.start());

      /* encode it */
      encoded.append(encodeChunk(before, charset, allowReservedCharacters));

      /* append the encoded value */
      encoded.append(matcher.group());

      /* update the string search index */
      index = matcher.end();
    } while (matcher.find());

    /* append the rest of the string */
    String tail = value.substring(index, length);
    encoded.append(encodeChunk(tail, charset, allowReservedCharacters));
    return encoded.toString();
  }

  /**
   * Encode a Uri Chunk, ensuring that all reserved characters are also encoded.
   *
   * @param value to encode.
   * @param charset to use.
   * @return an encoded uri chunk.
   */
  private static String encodeChunk(String value, Charset charset, boolean allowReserved) {
    if (isEncoded(value, charset)) {
      return value;
    }

    byte[] data = value.getBytes(charset);
    try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
      for (byte b : data) {
        if (isUnreserved((char) b)) {
          bos.write(b);
        } else if (isReserved((char) b) && allowReserved) {
          bos.write(b);
        } else {
          pctEncode(b, bos);
        }
      }
      return new String(bos.toByteArray(), charset);
    } catch (IOException ioe) {
      throw new IllegalStateException("Error occurred during encoding of the uri: "
          + ioe.getMessage(), ioe);
    }
  }

  /**
   * Percent Encode the provided byte.
   *
   * @param data to encode
   * @param bos with the output stream to use.
   */
  private static void pctEncode(byte data, ByteArrayOutputStream bos) {
    bos.write('%');
    char hex1 = Character.toUpperCase(Character.forDigit((data >> 4) & 0xF, 16));
    char hex2 = Character.toUpperCase(Character.forDigit(data & 0xF, 16));
    bos.write(hex1);
    bos.write(hex2);
  }



  private static boolean isAlpha(int c) {
    return (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z');
  }

  private static boolean isDigit(int c) {
    return (c >= '0' && c <= '9');
  }

  private static boolean isGenericDelimiter(int c) {
    return (c == ':') || (c == '/') || (c == '?') || (c == '#') || (c == '[') || (c == ']')
        || (c == '@');
  }

  private static boolean isSubDelimiter(int c) {
    return (c == '!') || (c == '$') || (c == '&') || (c == '\'') || (c == '(') || (c == ')')
        || (c == '*') || (c == '+') || (c == ',') || (c == ';') || (c == '=');
  }

  private static boolean isUnreserved(int c) {
    return isAlpha(c) || isDigit(c) || c == '-' || c == '.' || c == '_' || c == '~';
  }

  private static boolean isReserved(int c) {
    return isGenericDelimiter(c) || isSubDelimiter(c);
  }

  private boolean isPchar(int c) {
    return isUnreserved(c) || isSubDelimiter(c) || c == ':' || c == '@';
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy