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
60 changes: 50 additions & 10 deletions httpcore5/src/main/java/org/apache/hc/core5/net/PercentCodec.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.StandardCharsets;
import java.util.BitSet;

Expand Down Expand Up @@ -179,7 +180,22 @@ static void encode(final StringBuilder buf, final CharSequence content, final Ch
return;
}
final CharBuffer cb = CharBuffer.wrap(content);
final ByteBuffer bb = (charset != null ? charset : StandardCharsets.UTF_8).encode(cb);
ByteBuffer bb = null;
while (cb.hasRemaining()) {
final char ch = cb.get();
if (safechars.get(ch)) {
buf.append(ch);
} else if (blankAsPlus && ch == ' ') {
buf.append("+");
} else {
cb.position(cb.position() - 1);
bb = (charset != null ? charset : StandardCharsets.UTF_8).encode(cb);
break;
}
}
if (bb == null) {
return;
}
while (bb.hasRemaining()) {
final int b = bb.get() & 0xff;
if (safechars.get(b)) {
Expand Down Expand Up @@ -217,8 +233,10 @@ static String decode(final CharSequence content, final Charset charset, final bo
if (content == null) {
return null;
}
final ByteBuffer bb = ByteBuffer.allocate(content.length());
final CharBuffer cb = CharBuffer.wrap(content);
final CharBuffer result = CharBuffer.allocate(content.length());
ByteBuffer bb = null;
CharsetDecoder charsetDecoder = null;
while (cb.hasRemaining()) {
final char c = cb.get();
if (c == '%' && cb.remaining() >= 2) {
Expand All @@ -227,20 +245,42 @@ static String decode(final CharSequence content, final Charset charset, final bo
final int u = Character.digit(uc, RADIX);
final int l = Character.digit(lc, RADIX);
if (u != -1 && l != -1) {
if (bb == null) {
bb = ByteBuffer.allocate(cb.remaining() + 1);
}
if (charsetDecoder == null) {
charsetDecoder = (charset != null ? charset : StandardCharsets.UTF_8).newDecoder();
}
bb.put((byte) ((u << 4) + l));
} else {
bb.put((byte) '%');
bb.put((byte) uc);
bb.put((byte) lc);
flushBin(bb, result, charsetDecoder);
result.put('%');
result.put(uc);
result.put(lc);
}
} else if (plusAsBlank && c == '+') {
bb.put((byte) ' ');
} else {
bb.put((byte) c);
flushBin(bb, result, charsetDecoder);
if (plusAsBlank && c == '+') {
result.put(' ');
} else {
result.put(c);
}
}
}
bb.flip();
return (charset != null ? charset : StandardCharsets.UTF_8).decode(bb).toString();
flushBin(bb, result, charsetDecoder);
result.flip();
return result.toString();
}

static void flushBin(final ByteBuffer bb, final CharBuffer result, final CharsetDecoder charsetDecoder) {
if (bb == null) {
return;
}
if (bb.position() > 0) {
bb.flip();
charsetDecoder.decode(bb, result, true);
bb.compact();
}
}

public static String decode(final CharSequence content, final Charset charset) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,12 @@ void testPercentCodecEncodeIsNotRfc7639Canonical() {
assertEquals("%7C", PercentCodec.encode("|", StandardCharsets.UTF_8));
}

@Test
void decodeIllegalChars() {
final String s = "1\u012E2\u012E3\u012E4";
assertEquals(s, PercentCodec.decode(s, StandardCharsets.UTF_8));
assertEquals(s, PercentCodec.decode("1\u012E2%C4%AE3\u012E4", StandardCharsets.UTF_8));
assertEquals(s, PercentCodec.decode("1%C4%AE2%C4%AE3%C4%AE4", StandardCharsets.UTF_8));
}

}
Loading