diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/SessionOutputBufferImpl.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/SessionOutputBufferImpl.java index 77cd9951fa..0ef5611fda 100644 --- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/SessionOutputBufferImpl.java +++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/SessionOutputBufferImpl.java @@ -41,6 +41,7 @@ import org.apache.hc.core5.http.nio.SessionOutputBuffer; import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.CharArrayBuffer; +import org.apache.hc.core5.util.TextUtils; class SessionOutputBufferImpl extends ExpandableBuffer implements SessionOutputBuffer { @@ -171,12 +172,14 @@ public void writeLine(final CharArrayBuffer lineBuffer) throws CharacterCodingEx final int off = buffer().position(); final int arrayOffset = buffer().arrayOffset(); for (int i = 0; i < len; i++) { - b[arrayOffset + off + i] = (byte) lineBuffer.charAt(i); + final int c = lineBuffer.charAt(i); + b[arrayOffset + off + i] = TextUtils.castAsByte(c); } buffer().position(off + len); } else { for (int i = 0; i < lineBuffer.length(); i++) { - buffer().put((byte) lineBuffer.charAt(i)); + final int c = lineBuffer.charAt(i); + buffer().put(TextUtils.castAsByte(c)); } } } else { diff --git a/httpcore5/src/main/java/org/apache/hc/core5/util/ByteArrayBuffer.java b/httpcore5/src/main/java/org/apache/hc/core5/util/ByteArrayBuffer.java index c9479c320e..819ad7d3f4 100644 --- a/httpcore5/src/main/java/org/apache/hc/core5/util/ByteArrayBuffer.java +++ b/httpcore5/src/main/java/org/apache/hc/core5/util/ByteArrayBuffer.java @@ -138,14 +138,7 @@ public void append(final char[] b, final int off, final int len) { } for (int i1 = off, i2 = oldlen; i2 < newlen; i1++, i2++) { - final int c = b[i1]; - if ((c >= 0x20 && c <= 0x7E) || // Visible ASCII - (c >= 0xA0 && c <= 0xFF) || // Visible ISO-8859-1 - c == 0x09) { // TAB - this.array[i2] = (byte) c; - } else { - this.array[i2] = '?'; - } + this.array[i2] = TextUtils.castAsByte(b[i1]); } this.len = newlen; } diff --git a/httpcore5/src/main/java/org/apache/hc/core5/util/TextUtils.java b/httpcore5/src/main/java/org/apache/hc/core5/util/TextUtils.java index a8b6a6e280..db4c948bc9 100644 --- a/httpcore5/src/main/java/org/apache/hc/core5/util/TextUtils.java +++ b/httpcore5/src/main/java/org/apache/hc/core5/util/TextUtils.java @@ -29,6 +29,8 @@ import java.util.Locale; +import org.apache.hc.core5.annotation.Internal; + /** * @since 4.3 */ @@ -141,4 +143,21 @@ public static String toLowerCase(final String s) { return s.toLowerCase(Locale.ROOT); } + /** + * Casts character to byte filtering non-visible and non-ASCII characters + * before conversion + * + * @since 5.2 + */ + @Internal + public static byte castAsByte(final int c) { + if ((c >= 0x20 && c <= 0x7E) || // Visible ASCII + (c >= 0xA0 && c <= 0xFF) || // Visible ISO-8859-1 + c == 0x09) { // TAB + return (byte) c; + } else { + return '?'; + } + } + } diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/impl/nio/TestSessionInOutBuffers.java b/httpcore5/src/test/java/org/apache/hc/core5/http/impl/nio/TestSessionInOutBuffers.java index 7579a1fc53..0f4d1bba89 100644 --- a/httpcore5/src/test/java/org/apache/hc/core5/http/impl/nio/TestSessionInOutBuffers.java +++ b/httpcore5/src/test/java/org/apache/hc/core5/http/impl/nio/TestSessionInOutBuffers.java @@ -194,6 +194,34 @@ public void testWriteLineChunks() throws Exception { Assertions.assertEquals("One\r\nTwo\r\nThree\r\nFour\r\n", s); } + @Test + public void testNonASCIIWriteLine() throws Exception { + final String testString = "123\u010Anew-header-from-some-header:injected-value"; + final String expectedResult = "123?new-header-from-some-header:injected-value"; + + final CharArrayBuffer chbuffer = new CharArrayBuffer(32); + final SessionOutputBuffer outbuf = new SessionOutputBufferImpl(1024, 16); + + chbuffer.clear(); + chbuffer.append(testString); + outbuf.writeLine(chbuffer); + + //this write operation should have no effect + outbuf.writeLine(null); + + final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + final WritableByteChannel outChannel = newChannel(outStream); + outbuf.flush(outChannel); + + final ReadableByteChannel channel = newChannel(outStream.toByteArray()); + + final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 16, 0); + inbuf.fill(channel); + chbuffer.clear(); + inbuf.readLine(chbuffer, true); + Assertions.assertEquals(expectedResult, chbuffer.toString()); + } + @Test public void testBasicReadWriteLine() throws Exception {