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);

Reply via email to