Merge remote-tracking branch 'origin/jetty-8'

Conflicts:
	VERSION.txt
	aggregates/jetty-all/pom.xml
	examples/async-rest/async-rest-jar/pom.xml
	examples/async-rest/async-rest-webapp/pom.xml
	examples/async-rest/pom.xml
	examples/embedded/pom.xml
	jetty-aggregate/jetty-all-server/pom.xml
	jetty-aggregate/jetty-client/pom.xml
	jetty-aggregate/jetty-plus/pom.xml
	jetty-aggregate/jetty-servlet/pom.xml
	jetty-aggregate/jetty-webapp/pom.xml
	jetty-aggregate/jetty-websocket/pom.xml
	jetty-aggregate/pom.xml
	jetty-annotations/pom.xml
	jetty-client/pom.xml
	jetty-continuation/pom.xml
	jetty-deploy/pom.xml
	jetty-distribution/pom.xml
	jetty-http-spi/pom.xml
	jetty-http/pom.xml
	jetty-io/pom.xml
	jetty-jaspi/pom.xml
	jetty-jmx/pom.xml
	jetty-jndi/pom.xml
	jetty-jsp/pom.xml
	jetty-monitor/pom.xml
	jetty-nosql/pom.xml
	jetty-osgi/jetty-osgi-boot-jsp/pom.xml
	jetty-osgi/jetty-osgi-boot-warurl/pom.xml
	jetty-osgi/jetty-osgi-boot/pom.xml
	jetty-osgi/jetty-osgi-httpservice/pom.xml
	jetty-osgi/pom.xml
	jetty-osgi/test-jetty-osgi-context/pom.xml
	jetty-osgi/test-jetty-osgi-webapp/pom.xml
	jetty-osgi/test-jetty-osgi/pom.xml
	jetty-plus/pom.xml
	jetty-policy/pom.xml
	jetty-proxy/pom.xml
	jetty-rewrite/pom.xml
	jetty-runner/pom.xml
	jetty-security/pom.xml
	jetty-server/pom.xml
	jetty-servlet/pom.xml
	jetty-servlets/pom.xml
	jetty-spdy/pom.xml
	jetty-spdy/spdy-client/pom.xml
	jetty-spdy/spdy-core/pom.xml
	jetty-spdy/spdy-example-webapp/pom.xml
	jetty-spdy/spdy-server/pom.xml
	jetty-spring/pom.xml
	jetty-start/pom.xml
	jetty-util-ajax/pom.xml
	jetty-util/pom.xml
	jetty-util/src/main/java/org/eclipse/jetty/util/UrlEncoded.java
	jetty-util/src/main/java/org/eclipse/jetty/util/Utf8StringBuilder.java
	jetty-util/src/test/java/org/eclipse/jetty/util/URLEncodedTest.java
	jetty-webapp/pom.xml
	jetty-websocket/pom.xml
	jetty-xml/pom.xml
	pom.xml
	test-continuation/pom.xml
	test-jetty-nested/pom.xml
	test-jetty-servlet/pom.xml
	tests/pom.xml
	tests/test-integration/pom.xml
	tests/test-loginservice/pom.xml
	tests/test-sessions/pom.xml
	tests/test-sessions/test-hash-sessions/pom.xml
	tests/test-sessions/test-jdbc-sessions/pom.xml
	tests/test-sessions/test-mongodb-sessions/pom.xml
	tests/test-sessions/test-sessions-common/pom.xml
	tests/test-webapps/pom.xml
	tests/test-webapps/test-jetty-webapp/pom.xml
	tests/test-webapps/test-webapp-rfc2616/pom.xml
This commit is contained in:
Greg Wilkins 2013-02-01 22:15:44 +11:00
commit 626f10678c
8 changed files with 451 additions and 20 deletions

View File

@ -95,6 +95,101 @@ jetty-9.0.0.M3 - 20 November 2012
+ 393947 additional tests
+ 394143 add jetty-all aggregate via release profile
+ 394144 add jetty-jaspi
jetty-8.1.9.v20130131 - 31 January 2013
+ 362226 HttpConnection "wait" call causes thread resource exhaustion
+ 367638 throw exception for excess form keys
+ 381521 Only set Vary header when content could be compressed
+ 382237 support non java JSON classes
+ 391248 fixing localhost checking in statistics servlet
+ 391249 fix for invalid XML node dispatchedTimeMean in statistics servlet
+ 391345 fix missing br tag in statistics servlet
+ 391623 Add option to --stop to wait for target jetty to stop
+ 392417 Prevent Cookie parsing interpreting unicode chars
+ 392492 expect headers only examined for requests>=HTTP/1.1
+ 393075 1xx 204 and 304 ignore all headers suggesting content
+ 393158 java.lang.IllegalStateException when sending an empty InputStream
+ 393220 remove dead code from ServletHandler and log ServletExceptions in
warn instead of debug
+ 393947 additional tests
+ 393968 fix typo in javadoc
+ 394294 A web-bundle started before jetty-osgi should be deployed as a webapp
when jetty-osgi starts
+ 394514 Preserve URI parameters in sendRedirect
+ 394541 remove continuation jar from distro, add as dep to test-jetty-webapp
+ 394719 remove regex from classpath matching
+ 394811 Make JAASLoginService log login failures to DEBUG instead of WARN.
Same for some other exceptions.
+ 394829 Session can not be restored after SessionManager.setIdleSavePeriod
has saved the session
+ 394839 Allow multipart mime with no boundary
+ 394870 Make enablement of remote access to test webapp configurable in
override-web.xml
+ 395215 Multipart mime with just LF and no CRLF
+ 395380 add ValidUrlRule to jetty-rewrite
+ 395394 allow logging from boot classloader
+ 396253 FilterRegistration wrong order
+ 396459 Log specific message for empty request body for multipart mime
requests
+ 396500 HttpClient Exchange takes forever to complete when less content sent
than Content-Length
+ 396574 add JETTY_HOME as a location for pid to be found
+ 396886 MultiPartFilter strips bad escaping on filename="..."
+ 397110 Accept %uXXXX encodings in URIs
+ 397111 Tolerate empty or excessive whitespace preceeding MultiParts
+ 397112 Requests with byte-range throws NPE if requested file has no mimetype
(eg no file extension)
+ 397130 maxFormContentSize set in jetty.xml is ignored
+ 397190 improve ValidUrlRule to iterate on codepoints
+ 397321 Wrong condition in default start.config for annotations
+ 397535 Support pluggable alias checking to support symbolic links
+ 398337 UTF-16 percent encoding in UTF-16 form content
+ 399132 check parent dir of session store against file to be removed
+ JETTY-1533 handle URL with no path
jetty-7.6.9.v20130131 - 31 January 2013
+ 362226 HttpConnection "wait" call causes thread resource exhaustion
+ 367638 throw exception for excess form keys
+ 381521 Only set Vary header when content could be compressed
+ 382237 support non java JSON classes
+ 391248 fixing localhost checking in statistics servlet
+ 391249 fix for invalid XML node dispatchedTimeMean in statistics servlet
+ 391345 fix missing br tag in statistics servlet
+ 391623 Add option to --stop to wait for target jetty to stop
+ 392417 Prevent Cookie parsing interpreting unicode chars
+ 392492 expect headers only examined for requests>=HTTP/1.1
+ 393075 1xx 204 and 304 ignore all headers suggesting content
+ 393220 remove dead code from ServletHandler and log ServletExceptions in
warn instead of debug
+ 393947 additional tests
+ 393968 fix typo in javadoc
+ 394514 Preserve URI parameters in sendRedirect
+ 394541 remove continuation jar from distro, add as dep to test-jetty-webapp
+ 394719 remove regex from classpath matching
+ 394811 Make JAASLoginService log login failures to DEBUG instead of WARN.
Same for some other exceptions.
+ 394829 Session can not be restored after SessionManager.setIdleSavePeriod
has saved the session
+ 394839 Allow multipart mime with no boundary
+ 395215 Multipart mime with just LF and no CRLF
+ 395380 add ValidUrlRule to jetty-rewrite
+ 395394 allow logging from boot classloader
+ 396459 Log specific message for empty request body for multipart mime
requests
+ 396500 HttpClient Exchange takes forever to complete when less content sent
than Content-Length
+ 396574 add JETTY_HOME as a location for pid to be found
+ 396886 MultiPartFilter strips bad escaping on filename="..."
+ 397110 Accept %uXXXX encodings in URIs
+ 397111 Tolerate empty or excessive whitespace preceeding MultiParts
+ 397112 Requests with byte-range throws NPE if requested file has no mimetype
(eg no file extension)
+ 397130 maxFormContentSize set in jetty.xml is ignored
+ 397190 improve ValidUrlRule to iterate on codepoints
+ 397321 Wrong condition in default start.config for annotations
+ 397535 Support pluggable alias checking to support symbolic links
+ 398337 UTF-16 percent encoding in UTF-16 form content
+ 399132 check parent dir of session store against file to be removed
+ JETTY-1533 handle URL with no path
+ 394215 Scheduled tasks throwing exceptions kill java.util.Timer thread.
+ 394232 add jetty-ant into jetty9
+ 394357 Make JarResource constructors protected
@ -345,7 +440,7 @@ jetty-7.6.6.v20120903 - 03 September 2012
renewed
+ JETTY-1532 HTTP headers decoded with platform's default encoding
+ JETTY-1541 fixed different behaviour for single byte writes
+ 385925: make SslContextFactory.setProtocols and
+ 385925 make SslContextFactory.setProtocols and
SslContextFactory.setCipherSuites preserve the order of the given parameters
jetty-8.1.5.v20120716 - 16 June 2012

View File

@ -0,0 +1,126 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId>
<version>7.6.10-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-osgi-boot-logback</artifactId>
<name>Jetty :: OSGi :: Boot Logback</name>
<description>Jetty OSGi Boot Logback bundle</description>
<properties>
<bundle-symbolic-name>${project.groupId}.boot.logback</bundle-symbolic-name>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-boot</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.osgi</groupId>
<artifactId>org.eclipse.osgi</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.osgi</groupId>
<artifactId>org.eclipse.osgi.services</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>artifact-jar</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
<execution>
<id>test-jar</id>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
<configuration>
<archive>
<manifestFile>target/classes/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
<configuration>
<instructions>
<Bundle-SymbolicName>org.eclipse.jetty.osgi.boot.logback;singleton:=true</Bundle-SymbolicName>
<Bundle-Name>Jetty-OSGi-Logback Integration</Bundle-Name>
<Fragment-Host>org.eclipse.jetty.osgi.boot</Fragment-Host>
<Import-Package>
ch.qos.logback.access.jetty;version="[0.9,1.1)";resolution:=optional,
ch.qos.logback.access.jetty.v7;version="[0.9,1.1)";resolution:=optional,
ch.qos.logback.*;version="[0.9,1.1)",
org.osgi.framework.*,
org.slf4j.*,
*;resolution:=optional
</Import-Package>
<Export-Package>
!org.eclipse.jetty.osgi.boot.logback.internal.*,
org.eclipse.jetty.osgi.boot.logback.*;version="${parsedVersion.osgiVersion}"
</Export-Package>
<_nouses>true</_nouses>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<configuration>
<onlyAnalyze>org.eclipse.jetty.osgi.boot.logback.*</onlyAnalyze>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,120 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty.osgi</groupId>
<artifactId>jetty-osgi-project</artifactId>
<version>7.6.10-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-osgi-equinoxtools</artifactId>
<name>Jetty :: OSGi :: Example Equinox Tools</name>
<description>Jetty OSGi Example Equinox Tools</description>
<properties>
<bundle-symbolic-name>${project.groupId}.equinoxtools</bundle-symbolic-name>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-continuation</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.osgi</groupId>
<artifactId>org.eclipse.osgi</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.osgi</groupId>
<artifactId>org.eclipse.osgi.services</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>process-resources</phase>
<configuration>
<tasks>
<copy todir="target/classes/equinoxconsole">
<fileset dir="equinoxconsole" />
</copy>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>artifact-jar</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
<execution>
<id>test-jar</id>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
<configuration>
<archive>
<manifestFile>target/classes/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
<configuration>
<instructions>
<Bundle-SymbolicName>org.eclipse.jetty.osgi.equinoxtools</Bundle-SymbolicName>
<Bundle-Name>Console</Bundle-Name>
<Bundle-Activator>org.eclipse.jetty.osgi.equinoxtools.WebEquinoxToolsActivator</Bundle-Activator>
<Export-Package>org.eclipse.jetty.osgi.equinoxtools;x-internal:=true;version="${parsedVersion.osgiVersion}",
org.eclipse.jetty.osgi.equinoxtools.console;x-internal:=true;version="${parsedVersion.osgiVersion}"
</Export-Package>
<_nouses>true</_nouses>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<configuration>
<onlyAnalyze>org.eclipse.jetty.osgi.equinoxtools.*</onlyAnalyze>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -324,10 +324,20 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
i++;
if (i+4<end)
buffer.getStringBuilder().append(Character.toChars((convertHexDigit(raw[++i])<<12) +(convertHexDigit(raw[++i])<<8) + (convertHexDigit(raw[++i])<<4) +convertHexDigit(raw[++i])));
else
{
buffer.getStringBuilder().append(Utf8Appendable.REPLACEMENT);
i=end;
}
}
else
buffer.append((byte)((convertHexDigit(raw[++i])<<4) + convertHexDigit(raw[++i])));
}
else
{
buffer.getStringBuilder().append(Utf8Appendable.REPLACEMENT);
i=end;
}
break;
default:
@ -344,13 +354,13 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
if (key != null)
{
value = buffer.length()==0?"":buffer.toString();
value = buffer.length()==0?"":buffer.toReplacedString();
buffer.reset();
map.add(key,value);
}
else if (buffer.length()>0)
{
map.add(buffer.toString(),"");
map.add(buffer.toReplacedString(),"");
}
}
}
@ -770,7 +780,10 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
buffer.getStringBuffer().append(unicode);
}
else
{
i=length;
buffer.getStringBuffer().append(Utf8Appendable.REPLACEMENT);
}
}
else
{
@ -780,13 +793,22 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
buffer.append(b);
}
}
catch(NotUtf8Exception e)
{
LOG.warn(e.toString());
LOG.debug(e);
}
catch(NumberFormatException nfe)
{
LOG.debug(nfe);
buffer.getStringBuffer().append(Utf8Appendable.REPLACEMENT);
}
}
else
{
buffer.getStringBuffer().append(Utf8Appendable.REPLACEMENT);
i=length;
}
}
else if (buffer!=null)
buffer.getStringBuffer().append(c);
@ -799,7 +821,7 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
return encoded.substring(offset,offset+length);
}
return buffer.toString();
return buffer.toReplacedString();
}
else
{
@ -848,6 +870,8 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
{
if ('u'==encoded.charAt(offset+i+1))
{
if (i+6<length)
{
int o=offset+i+2;
i+=6;
String unicode = new String(Character.toChars(TypeUtil.parseInt(encoded,o,4,16)));
@ -857,6 +881,12 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
}
else
{
ba[n++] = (byte)'?';
i=length;
}
}
else
{
int o=offset+i+1;
i+=3;
ba[n]=(byte)TypeUtil.parseInt(encoded,o,2,16);
@ -871,8 +901,8 @@ public class UrlEncoded extends MultiMap<String> implements Cloneable
}
else
{
ba[n++] = (byte)'%';
i++;
ba[n++] = (byte)'?';
i=length;
}
}
else if (c=='+')

View File

@ -21,6 +21,9 @@ package org.eclipse.jetty.util;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/* ------------------------------------------------------------ */
/**
* Utf8 Appendable abstract base class
@ -47,6 +50,7 @@ import java.nio.ByteBuffer;
**/
public abstract class Utf8Appendable
{
protected static final Logger LOG = Log.getLogger(Utf8Appendable.class);
public static final char REPLACEMENT = '\ufffd';
private static final int UTF8_ACCEPT = 0;
private static final int UTF8_REJECT = 12;
@ -209,4 +213,43 @@ public abstract class Utf8Appendable
super("Not valid UTF8! "+reason);
}
}
protected void checkState()
{
if (!isUtf8SequenceComplete())
{
_codep=0;
_state = UTF8_ACCEPT;
try
{
_appendable.append(REPLACEMENT);
}
catch(IOException e)
{
throw new RuntimeException(e);
}
throw new NotUtf8Exception("incomplete UTF8 sequence");
}
}
public String toReplacedString()
{
if (!isUtf8SequenceComplete())
{
_codep=0;
_state = UTF8_ACCEPT;
try
{
_appendable.append(REPLACEMENT);
}
catch(IOException e)
{
throw new RuntimeException(e);
}
Throwable th= new NotUtf8Exception("incomplete UTF8 sequence");
LOG.warn(th.toString());
LOG.debug(th);
}
return _appendable.toString();
}
}

View File

@ -72,10 +72,4 @@ public class Utf8StringBuffer extends Utf8Appendable
checkState();
return _buffer.toString();
}
private void checkState()
{
if (!isUtf8SequenceComplete())
throw new IllegalArgumentException("Tried to read incomplete UTF8 decoded String");
}
}

View File

@ -74,9 +74,5 @@ public class Utf8StringBuilder extends Utf8Appendable
return _buffer.toString();
}
private void checkState()
{
if (!isUtf8SequenceComplete())
throw new NotUtf8Exception("Tried to read incomplete UTF8 decoded String");
}
}

View File

@ -160,11 +160,38 @@ public class URLEncodedTest
public void testBadEncoding()
{
UrlEncoded url_encoded = new UrlEncoded();
url_encoded.decode("Name15=xx%zz", StringUtil.__UTF8_CHARSET);
url_encoded.decode("Name15=xx%zzyy", StringUtil.__UTF8_CHARSET);
assertEquals("encoded param size",1, url_encoded.size());
assertEquals("encoded get", "xx\ufffd", url_encoded.getString("Name15"));
assertEquals("encoded get", "xx\ufffdyy", url_encoded.getString("Name15"));
assertEquals("xxx",UrlEncoded.decodeString("xxx%u123",0,5,StringUtil.__UTF8_CHARSET));
byte[] bad="Name=%FF%FF%FF".getBytes(StringUtil.__UTF8_CHARSET);
MultiMap<String> map = new MultiMap<String>();
UrlEncoded.decodeUtf8To(bad,0,bad.length,map);
assertEquals("encoded param size",1, map.size());
assertEquals("encoded get", "\ufffd\ufffd\ufffd", map.getString("Name"));
url_encoded.clear();
url_encoded.decode("Name=%FF%FF%FF", StringUtil.__UTF8_CHARSET);
assertEquals("encoded param size",1, url_encoded.size());
assertEquals("encoded get", "\ufffd\ufffd\ufffd", url_encoded.getString("Name"));
url_encoded.clear();
url_encoded.decode("Name=%EF%EF%EF", StringUtil.__UTF8_CHARSET);
assertEquals("encoded param size",1, url_encoded.size());
assertEquals("encoded get", "\ufffd\ufffd", url_encoded.getString("Name"));
assertEquals("x",UrlEncoded.decodeString("x",0,1,StringUtil.__UTF8_CHARSET));
assertEquals("x\ufffd",UrlEncoded.decodeString("x%",0,2,StringUtil.__UTF8_CHARSET));
assertEquals("x\ufffd",UrlEncoded.decodeString("x%2",0,3,StringUtil.__UTF8_CHARSET));
assertEquals("x ",UrlEncoded.decodeString("x%20",0,4,StringUtil.__UTF8_CHARSET));
assertEquals("xxx",UrlEncoded.decodeString("xxx",0,3,StringUtil.__UTF8_CHARSET));
assertEquals("xxx\ufffd",UrlEncoded.decodeString("xxx%",0,4,StringUtil.__UTF8_CHARSET));
assertEquals("xxx\ufffd",UrlEncoded.decodeString("xxx%u",0,5,StringUtil.__UTF8_CHARSET));
assertEquals("xxx\ufffd",UrlEncoded.decodeString("xxx%u1",0,6,StringUtil.__UTF8_CHARSET));
assertEquals("xxx\ufffd",UrlEncoded.decodeString("xxx%u12",0,7,StringUtil.__UTF8_CHARSET));
assertEquals("xxx\ufffd",UrlEncoded.decodeString("xxx%u123",0,8,StringUtil.__UTF8_CHARSET));
assertEquals("xxx\u1234",UrlEncoded.decodeString("xxx%u1234",0,9,StringUtil.__UTF8_CHARSET));
}