Repository: netty/netty
Issue: Encode URI using UTF-8 charset
Labels: defect

Starting with IE7, all URLs are encoded using the UTF-8 character set (http://support.microsoft.com/kb/934279).

Netty assumes that the request uri string is encoded as ASCII -- see HttpRequestEncoder: buf.writeBytes(request.getUri().getBytes("ASCII"))

Requests that are generated by HttpMessageDecoder use StringBuilder to generate the URI and so will not preserve UTF-8 characters when encoded using HttpRequestEncoder.

Since UTF-8 is a strict superset of ASCII, we should be able to encode the URI using UTF-8 without breaking any requests whose URI only contains ASCII characters.


Issue comments:
@jpinner makes sense.. I want to cut 3.5.4.Final tomorrow. Would be nice if you could "fix it" before that..


Pull request review comments:

Patch that resolved the issue:
diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessageEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessageEncoder.java
index c83c1921904..b1c56222325 100644
--- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessageEncoder.java
+++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessageEncoder.java
@@ -150,10 +150,10 @@ private static void encodeTrailingHeaders(ByteBuf buf, HttpChunkTrailer trailer)
 
     private static void encodeHeader(ByteBuf buf, String header, String value)
             throws UnsupportedEncodingException {
-        buf.writeBytes(header.getBytes("ASCII"));
+        buf.writeBytes(header.getBytes(CharsetUtil.US_ASCII));
         buf.writeByte(COLON);
         buf.writeByte(SP);
-        buf.writeBytes(value.getBytes("ASCII"));
+        buf.writeBytes(value.getBytes(CharsetUtil.US_ASCII));
         buf.writeByte(CR);
         buf.writeByte(LF);
     }
diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequestEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequestEncoder.java
index eda340707e7..f450c809da2 100644
--- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequestEncoder.java
+++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequestEncoder.java
@@ -17,6 +17,7 @@
 
 import static io.netty.handler.codec.http.HttpConstants.*;
 import io.netty.buffer.ByteBuf;
+import io.netty.util.CharsetUtil;
 
 /**
  * Encodes an {@link HttpRequest} or an {@link HttpChunk} into
@@ -33,11 +34,11 @@ public HttpRequestEncoder() {
     @Override
     protected void encodeInitialLine(ByteBuf buf, HttpMessage message) throws Exception {
         HttpRequest request = (HttpRequest) message;
-        buf.writeBytes(request.getMethod().toString().getBytes("ASCII"));
+        buf.writeBytes(request.getMethod().toString().getBytes(CharsetUtil.US_ASCII));
         buf.writeByte(SP);
-        buf.writeBytes(request.getUri().getBytes("ASCII"));
+        buf.writeBytes(request.getUri().getBytes(CharsetUtil.UTF_8));
         buf.writeByte(SP);
-        buf.writeBytes(request.getProtocolVersion().toString().getBytes("ASCII"));
+        buf.writeBytes(request.getProtocolVersion().toString().getBytes(CharsetUtil.US_ASCII));
         buf.writeByte(CR);
         buf.writeByte(LF);
     }
diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseEncoder.java
index d897f680dd9..141b41d8e19 100644
--- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseEncoder.java
+++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponseEncoder.java
@@ -17,6 +17,7 @@
 
 import static io.netty.handler.codec.http.HttpConstants.*;
 import io.netty.buffer.ByteBuf;
+import io.netty.util.CharsetUtil;
 
 /**
  * Encodes an {@link HttpResponse} or an {@link HttpChunk} into
@@ -33,11 +34,11 @@ public HttpResponseEncoder() {
     @Override
     protected void encodeInitialLine(ByteBuf buf, HttpMessage message) throws Exception {
         HttpResponse response = (HttpResponse) message;
-        buf.writeBytes(response.getProtocolVersion().toString().getBytes("ASCII"));
+        buf.writeBytes(response.getProtocolVersion().toString().getBytes(CharsetUtil.US_ASCII));
         buf.writeByte(SP);
-        buf.writeBytes(String.valueOf(response.getStatus().getCode()).getBytes("ASCII"));
+        buf.writeBytes(String.valueOf(response.getStatus().getCode()).getBytes(CharsetUtil.US_ASCII));
         buf.writeByte(SP);
-        buf.writeBytes(String.valueOf(response.getStatus().getReasonPhrase()).getBytes("ASCII"));
+        buf.writeBytes(String.valueOf(response.getStatus().getReasonPhrase()).getBytes(CharsetUtil.US_ASCII));
         buf.writeByte(CR);
         buf.writeByte(LF);
     }
diff --git a/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspRequestEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspRequestEncoder.java
index 4a6815fb538..d968c3c88be 100644
--- a/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspRequestEncoder.java
+++ b/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspRequestEncoder.java
@@ -18,6 +18,7 @@
 import io.netty.buffer.ByteBuf;
 import io.netty.handler.codec.http.HttpMessage;
 import io.netty.handler.codec.http.HttpRequest;
+import io.netty.util.CharsetUtil;
 
 /**
  * Encodes an RTSP request represented in {@link HttpRequest} into
@@ -30,11 +31,11 @@ public class RtspRequestEncoder extends RtspMessageEncoder {
     protected void encodeInitialLine(ByteBuf buf, HttpMessage message)
             throws Exception {
         HttpRequest request = (HttpRequest) message;
-        buf.writeBytes(request.getMethod().toString().getBytes("ASCII"));
+        buf.writeBytes(request.getMethod().toString().getBytes(CharsetUtil.US_ASCII));
         buf.writeByte((byte) ' ');
-        buf.writeBytes(request.getUri().getBytes("ASCII"));
+        buf.writeBytes(request.getUri().getBytes(CharsetUtil.US_ASCII));
         buf.writeByte((byte) ' ');
-        buf.writeBytes(request.getProtocolVersion().toString().getBytes("ASCII"));
+        buf.writeBytes(request.getProtocolVersion().toString().getBytes(CharsetUtil.US_ASCII));
         buf.writeByte((byte) '\r');
         buf.writeByte((byte) '\n');
     }
diff --git a/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspResponseEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspResponseEncoder.java
index 63b94c5df70..e5c2f719a46 100644
--- a/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspResponseEncoder.java
+++ b/codec-http/src/main/java/io/netty/handler/codec/rtsp/RtspResponseEncoder.java
@@ -18,6 +18,7 @@
 import io.netty.buffer.ByteBuf;
 import io.netty.handler.codec.http.HttpMessage;
 import io.netty.handler.codec.http.HttpResponse;
+import io.netty.util.CharsetUtil;
 
 /**
  * Encodes an RTSP response represented in {@link HttpResponse} into
@@ -30,11 +31,11 @@ public class RtspResponseEncoder extends RtspMessageEncoder {
     protected void encodeInitialLine(ByteBuf buf, HttpMessage message)
             throws Exception {
         HttpResponse response = (HttpResponse) message;
-        buf.writeBytes(response.getProtocolVersion().toString().getBytes("ASCII"));
+        buf.writeBytes(response.getProtocolVersion().toString().getBytes(CharsetUtil.US_ASCII));
         buf.writeByte((byte) ' ');
-        buf.writeBytes(String.valueOf(response.getStatus().getCode()).getBytes("ASCII"));
+        buf.writeBytes(String.valueOf(response.getStatus().getCode()).getBytes(CharsetUtil.US_ASCII));
         buf.writeByte((byte) ' ');
-        buf.writeBytes(String.valueOf(response.getStatus().getReasonPhrase()).getBytes("ASCII"));
+        buf.writeBytes(String.valueOf(response.getStatus().getReasonPhrase()).getBytes(CharsetUtil.US_ASCII));
         buf.writeByte((byte) '\r');
         buf.writeByte((byte) '\n');
     }
diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyCodecUtil.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyCodecUtil.java
index 8d4059a399f..5458a9f77f2 100644
--- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyCodecUtil.java
+++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyCodecUtil.java
@@ -16,6 +16,7 @@
 package io.netty.handler.codec.spdy;
 
 import io.netty.buffer.ByteBuf;
+import io.netty.util.CharsetUtil;
 
 final class SpdyCodecUtil {
 
@@ -251,7 +252,7 @@ final class SpdyCodecUtil {
         byte[] SPDY2_DICT_;
 
         try {
-            SPDY2_DICT_ = SPDY2_DICT_S.getBytes("US-ASCII");
+            SPDY2_DICT_ = SPDY2_DICT_S.getBytes(CharsetUtil.US_ASCII);
             // dictionary is null terminated
             SPDY2_DICT_[SPDY2_DICT_.length - 1] = (byte) 0;
         } catch (Exception e) {
diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyFrameEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyFrameEncoder.java
index 350a12f20f8..f05b4c469ad 100644
--- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyFrameEncoder.java
+++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyFrameEncoder.java
@@ -23,6 +23,7 @@
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.handler.codec.MessageToByteEncoder;
 import io.netty.handler.codec.UnsupportedMessageTypeException;
+import io.netty.util.CharsetUtil;
 
 import java.util.Set;
 
@@ -302,14 +303,14 @@ private static ByteBuf encodeHeaderBlock(int version, SpdyHeaderBlock headerFram
         ByteBuf headerBlock = Unpooled.buffer();
         writeLengthField(version, headerBlock, numHeaders);
         for (String name: names) {
-            byte[] nameBytes = name.getBytes("UTF-8");
+            byte[] nameBytes = name.getBytes(CharsetUtil.UTF_8);
             writeLengthField(version, headerBlock, nameBytes.length);
             headerBlock.writeBytes(nameBytes);
             int savedIndex = headerBlock.writerIndex();
             int valueLength = 0;
             writeLengthField(version, headerBlock, valueLength);
             for (String value: headerFrame.getHeaders(name)) {
-                byte[] valueBytes = value.getBytes("UTF-8");
+                byte[] valueBytes = value.getBytes(CharsetUtil.UTF_8);
                 headerBlock.writeBytes(valueBytes);
                 headerBlock.writeByte(0);
                 valueLength += valueBytes.length + 1;


Test patch: