This is an automated email from the ASF dual-hosted git repository.
garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-codec.git
The following commit(s) were added to refs/heads/master by this push:
new 8194db4c [CODEC-338] Escape literal plus with plusForSpace (#432)
8194db4c is described below
commit 8194db4cdf9e5c3d7831d6ad750dd9217b86b754
Author: OldTruckDriver <[email protected]>
AuthorDate: Thu Jun 18 11:28:14 2026 +1000
[CODEC-338] Escape literal plus with plusForSpace (#432)
When plusForSpace is enabled, decoding treats '+' as a space. Mark '+' as
an always-encoded character for that mode so encoding emits a literal plus as
%2B and preserves round trips.
Reviewed-by: OpenAI Codex
Reviewed-by: Anthropic Claude Code
---
src/changes/changes.xml | 1 +
.../org/apache/commons/codec/net/PercentCodec.java | 8 ++++++++
.../apache/commons/codec/net/PercentCodecTest.java | 22 ++++++++++++++++++++++
3 files changed, 31 insertions(+)
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 3dc2f6c4..b0428fc4 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -45,6 +45,7 @@ The <action> type attribute can be add,update,fix,remove.
<body>
<release version="1.22.1" date="YYYY-MM-DD" description="This is a feature
and maintenance release. Java 8 or later is required.">
<!-- FIX -->
+ <action type="fix" issue="CODEC-338" dev="ggregory" due-to="Ruiqi Dong,
Gary Gregory">PercentCodec loses literal '+' when plusForSpace is
enabled.</action>
<action type="add" issue="CODEC-337" dev="pkarwasz" due-to="Ruiqi Dong,
Gary Gregory">Digest ALL reuses System.in, so only the first algorithm sees the
real input (#431).</action>
<!-- ADD -->
<!-- UPDATE -->
diff --git a/src/main/java/org/apache/commons/codec/net/PercentCodec.java
b/src/main/java/org/apache/commons/codec/net/PercentCodec.java
index 1d11b096..c7244397 100644
--- a/src/main/java/org/apache/commons/codec/net/PercentCodec.java
+++ b/src/main/java/org/apache/commons/codec/net/PercentCodec.java
@@ -44,6 +44,11 @@ public class PercentCodec implements BinaryEncoder,
BinaryDecoder {
*/
private static final byte ESCAPE_CHAR = '%';
+ /**
+ * The plus character used to encode spaces when plusForSpace is true.
+ */
+ private static final byte PLUS_CHAR = '+';
+
/**
* The bit set used to store the character that should be always encoded.
*/
@@ -80,6 +85,9 @@ public class PercentCodec implements BinaryEncoder,
BinaryDecoder {
public PercentCodec(final byte[] alwaysEncodeChars, final boolean
plusForSpace) {
this.plusForSpace = plusForSpace;
insertAlwaysEncodeChars(alwaysEncodeChars);
+ if (plusForSpace) {
+ insertAlwaysEncodeChar(PLUS_CHAR);
+ }
}
private boolean canEncode(final byte c) {
diff --git a/src/test/java/org/apache/commons/codec/net/PercentCodecTest.java
b/src/test/java/org/apache/commons/codec/net/PercentCodecTest.java
index 960aac14..ece15c16 100644
--- a/src/test/java/org/apache/commons/codec/net/PercentCodecTest.java
+++ b/src/test/java/org/apache/commons/codec/net/PercentCodecTest.java
@@ -131,6 +131,28 @@ class PercentCodecTest {
assertEquals(new String(decode, StandardCharsets.UTF_8), input,
"PercentCodec plus for space decoding test");
}
+ @Test
+ void testPercentEncoderDecoderWithPlusForSpaceEscapesLiteralPlus() throws
Exception {
+ final String input = "a+b c";
+ final PercentCodec percentCodec = new PercentCodec(null, true);
+ final byte[] encoded =
percentCodec.encode(input.getBytes(StandardCharsets.UTF_8));
+ final String encodedS = new String(encoded, StandardCharsets.UTF_8);
+ assertEquals("a%2Bb+c", encodedS, "PercentCodec plus for space should
escape literal plus");
+ final byte[] decode = percentCodec.decode(encoded);
+ assertEquals(new String(decode, StandardCharsets.UTF_8), input,
"PercentCodec literal plus decoding test");
+ }
+
+ @Test
+ void
testPercentEncoderDecoderWithPlusForSpaceEscapesLiteralPlusWithoutSpaces()
throws Exception {
+ final String input = "a+b";
+ final PercentCodec percentCodec = new PercentCodec(null, true);
+ final byte[] encoded =
percentCodec.encode(input.getBytes(StandardCharsets.UTF_8));
+ final String encodedS = new String(encoded, StandardCharsets.UTF_8);
+ assertEquals("a%2Bb", encodedS, "PercentCodec plus for space should
escape literal plus without spaces");
+ final byte[] decode = percentCodec.decode(encoded);
+ assertEquals(new String(decode, StandardCharsets.UTF_8), input,
"PercentCodec literal plus decoding test");
+ }
+
@Test
void testSafeCharEncodeDecodeObject() throws Exception {
final PercentCodec percentCodec = new PercentCodec(null, true);