Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 45 additions & 1 deletion src/main/java/org/apache/commons/codec/net/URLCodec.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.UnsupportedCharsetException;
import java.util.BitSet;

import org.apache.commons.codec.BinaryDecoder;
Expand Down Expand Up @@ -331,13 +337,51 @@ public String decode(final String str) throws DecoderException {
* Thrown if URL decoding is unsuccessful.
* @throws UnsupportedEncodingException
* Thrown if charset is not supported.
* @see #decode(String, String, boolean)
*/
public String decode(final String str, final String charsetName)
throws DecoderException, UnsupportedEncodingException {
return decode(str, charsetName, false);
}

/**
* Decodes a URL safe string using the specified character encoding.
* <p>
* When {@code strict} is {@code false}, malformed or unmappable byte sequences are handled as by
* {@link String#String(byte[], String)}. When it is {@code true}, they cause a {@link DecoderException}.
* </p>
*
* @param str URL safe string to decode, or {@code null}.
* @param charsetName name of the character encoding used to decode the result.
* @param strict whether malformed or unmappable byte sequences should be rejected.
* @return the decoded string, or {@code null} if {@code str} is {@code null}.
* @throws DecoderException if URL decoding fails or strict character decoding rejects the decoded bytes.
* @throws UnsupportedEncodingException if the named charset is not supported.
* @since 1.23.1
*/
public String decode(final String str, final String charsetName, final boolean strict)
throws DecoderException, UnsupportedEncodingException {
if (str == null) {
return null;
}
return new String(decode(StringUtils.getBytesUsAscii(str)), charsetName);
final byte[] decoded = decode(StringUtils.getBytesUsAscii(str));
if (!strict) {
return new String(decoded, charsetName);
}
final Charset charset;
try {
charset = Charset.forName(charsetName);
} catch (final IllegalCharsetNameException | UnsupportedCharsetException e) {
final UnsupportedEncodingException exception = new UnsupportedEncodingException(charsetName);
exception.initCause(e);
throw exception;
}
try {
return charset.newDecoder().onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT).decode(ByteBuffer.wrap(decoded)).toString();
} catch (final CharacterCodingException e) {
throw new DecoderException("Invalid byte sequence for charset " + charsetName, e);
}
}

/**
Expand Down
15 changes: 15 additions & 0 deletions src/test/java/org/apache/commons/codec/net/URLCodecTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.BitSet;

Expand Down Expand Up @@ -116,6 +117,20 @@ void testDecodeStringWithNull() throws Exception {
assertNull(result, "Result should be null");
}

@Test
void testDecodeStringWithStrictCharsetValidation() throws Exception {
final URLCodec urlCodec = new URLCodec();
final String malformedUtf8 = "%AE%8A-";
final String utf8 = StandardCharsets.UTF_8.name();

assertEquals("\uFFFD\uFFFD-", urlCodec.decode(malformedUtf8, utf8));
assertThrows(DecoderException.class, () -> urlCodec.decode(malformedUtf8, utf8, true));
assertEquals("Grüße", urlCodec.decode("Gr%C3%BC%C3%9Fe", utf8, true));
assertEquals("\u00AE\u008A-", urlCodec.decode(malformedUtf8, StandardCharsets.ISO_8859_1.name(), true));
assertNull(urlCodec.decode(null, utf8, true));
assertThrows(UnsupportedEncodingException.class, () -> urlCodec.decode(malformedUtf8, "NONSENSE", true));
}

@Test
void testDecodeUrlWithCustomBitSetAmbiguousEscapes() throws Exception {
final BitSet safe = new BitSet();
Expand Down