Skip to content

Commit

Permalink
UNDERTOW-1221 Fix issues with previous commit
Browse files Browse the repository at this point in the history
  • Loading branch information
stuartwdouglas committed Nov 4, 2017
1 parent 34ba18e commit 211cf2b
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 124 deletions.
Expand Up @@ -79,8 +79,7 @@ class AjpRequestParseState {
/** /**
* The current string being read * The current string being read
*/ */
private byte[] currentString = new byte[16]; private StringBuilder currentString = new StringBuilder();
private int currentStringLength = 0;


/** /**
* when reading the first byte of an integer this stores the first value. It is set to -1 to signify that * when reading the first byte of an integer this stores the first value. It is set to -1 to signify that
Expand All @@ -98,10 +97,10 @@ class AjpRequestParseState {


public void reset() { public void reset() {
stringLength = -1; stringLength = -1;
currentStringLength = 0;
currentIntegerPart = -1; currentIntegerPart = -1;
readHeaders = 0; readHeaders = 0;
badRequest = false; badRequest = false;
currentString.setLength(0);
} }
public boolean isComplete() { public boolean isComplete() {
return state == 15; return state == 15;
Expand Down Expand Up @@ -144,21 +143,16 @@ InetSocketAddress createDestinationAddress() {
} }


public void addStringByte(byte b) { public void addStringByte(byte b) {
if(currentString.length == currentStringLength) { currentString.append((char)b);
byte[] old = currentString;
currentString = new byte[currentStringLength + 16];
System.arraycopy(old, 0, currentString, 0, currentStringLength);
}
currentString[currentStringLength++] = b;
} }


public String getStringAndClear(String charset) throws UnsupportedEncodingException { public String getStringAndClear() throws UnsupportedEncodingException {
String ret = new String(currentString, 0, currentStringLength, charset); String ret = currentString.toString();
currentStringLength = 0; currentString.setLength(0);
return ret; return ret;
} }


public int getCurrentStringLength() { public int getCurrentStringLength() {
return currentStringLength; return currentString.length();
} }
} }
Expand Up @@ -535,7 +535,7 @@ protected StringHolder parseString(ByteBuffer buf, AjpRequestParseState state, S


if (buf.hasRemaining()) { if (buf.hasRemaining()) {
buf.get(); //null terminator buf.get(); //null terminator
String value = state.getStringAndClear(encoding); String value = state.getStringAndClear();
state.stringLength = -1; state.stringLength = -1;
state.containsUrlCharacters = false; state.containsUrlCharacters = false;
return new StringHolder(value, true, containsUrlCharacters); return new StringHolder(value, true, containsUrlCharacters);
Expand Down
205 changes: 107 additions & 98 deletions core/src/main/java/io/undertow/util/URLUtils.java
Expand Up @@ -18,11 +18,11 @@


package io.undertow.util; package io.undertow.util;


import java.io.UnsupportedEncodingException;

import io.undertow.UndertowMessages; import io.undertow.UndertowMessages;
import io.undertow.server.HttpServerExchange; import io.undertow.server.HttpServerExchange;


import java.io.UnsupportedEncodingException;

/** /**
* Utilities for dealing with URLs * Utilities for dealing with URLs
* *
Expand Down Expand Up @@ -90,24 +90,21 @@ public static String decode(String s, String enc, boolean decodeSlash, boolean f
int numChars = s.length(); int numChars = s.length();
int i = 0; int i = 0;


char c;
byte[] bytes = null;
while (i < numChars) { while (i < numChars) {
c = s.charAt(i); char c = s.charAt(i);
switch (c) { if (c == '+') {
case '+': if (formEncoding) {
if(formEncoding) { buffer.append(' ');
buffer.append(' '); i++;
i++; needToChange = true;
needToChange = true; } else {
} else { i++;
i++; buffer.append(c);
buffer.append(c); }
} } else if (c == '%' || c > 127) {
break;
case '%':
/* /*
* Starting with this instance of %, process all * Starting with this instance of a character
* that needs to be encoded, process all
* consecutive substrings of the form %xy. Each * consecutive substrings of the form %xy. Each
* substring %xy will yield a byte. Convert all * substring %xy will yield a byte. Convert all
* consecutive bytes obtained this way to whatever * consecutive bytes obtained this way to whatever
Expand All @@ -118,103 +115,115 @@ public static String decode(String s, String enc, boolean decodeSlash, boolean f
* three characters. For multi code point characters there if the code point can be * three characters. For multi code point characters there if the code point can be
* represented as an alphanumeric * represented as an alphanumeric
*/ */
try { try {
// (numChars-i) is an upper bound for the number // guess the size of the remaining bytes
// of remaining bytes // of remaining bytes
if (bytes == null) { // this works for percent encoded characters,
bytes = new byte[numChars - i + 1]; // not so much for unencoded bytes
} byte[] bytes = new byte[numChars - i + 1];
int pos = 0;

while ((i< numChars)) {
if (c == '%') {
char p1 = Character.toLowerCase(s.charAt(i + 1));
char p2 = Character.toLowerCase(s.charAt(i + 2));
if (!decodeSlash && ((p1 == '2' && p2 == 'f') || (p1 == '5' && p2 == 'c'))) {
bytes[pos++] = (byte) c;
// should be copied with preserved upper/lower case
bytes[pos++] = (byte) s.charAt(i + 1);
bytes[pos++] = (byte) s.charAt(i + 2);
i += 3;

if (i < numChars) {
c = s.charAt(i);
}
continue;
}
int v = 0;
if (p1 >= '0' && p1 <= '9') {
v = (p1 - '0') << 4;
} else if (p1 >= 'a' && p1 <= 'f') {
v = (p1 - 'a' + 10) << 4;
} else {
throw UndertowMessages.MESSAGES.failedToDecodeURL(s, enc, null);
}
if (p2 >= '0' && p2 <= '9') {
v += (p2 - '0');
} else if (p2 >= 'a' && p2 <= 'f') {
v += (p2 - 'a' + 10);
} else {
throw UndertowMessages.MESSAGES.failedToDecodeURL(s, enc, null);
}
if (v < 0) {
throw UndertowMessages.MESSAGES.failedToDecodeURL(s, enc, null);
}


bytes[pos++] = (byte) v; int pos = 0;

while ((i < numChars)) {
if (c == '%') {
char p1 = Character.toLowerCase(s.charAt(i + 1));
char p2 = Character.toLowerCase(s.charAt(i + 2));
if (!decodeSlash && ((p1 == '2' && p2 == 'f') || (p1 == '5' && p2 == 'c'))) {
if(pos + 2 >= bytes.length) {
bytes = expandBytes(bytes);
}
bytes[pos++] = (byte) c;
// should be copied with preserved upper/lower case
bytes[pos++] = (byte) s.charAt(i + 1);
bytes[pos++] = (byte) s.charAt(i + 2);
i += 3; i += 3;

if (i < numChars) { if (i < numChars) {
c = s.charAt(i); c = s.charAt(i);
} }
}else if(c == '+') { continue;
bytes[pos++] = (byte) ' '; }
++i; int v = 0;
if (i < numChars) { if (p1 >= '0' && p1 <= '9') {
c = s.charAt(i); v = (p1 - '0') << 4;
} else if (p1 >= 'a' && p1 <= 'f') {
v = (p1 - 'a' + 10) << 4;
} else {
throw UndertowMessages.MESSAGES.failedToDecodeURL(s, enc, null);
}
if (p2 >= '0' && p2 <= '9') {
v += (p2 - '0');
} else if (p2 >= 'a' && p2 <= 'f') {
v += (p2 - 'a' + 10);
} else {
throw UndertowMessages.MESSAGES.failedToDecodeURL(s, enc, null);
}
if (v < 0) {
throw UndertowMessages.MESSAGES.failedToDecodeURL(s, enc, null);
}

if(pos == bytes.length) {
bytes = expandBytes(bytes);
}
bytes[pos++] = (byte) v;
i += 3;
if (i < numChars) {
c = s.charAt(i);
}
} else if (c == '+' && formEncoding) {
if(pos == bytes.length) {
bytes = expandBytes(bytes);
}
bytes[pos++] = (byte) ' ';
++i;
if (i < numChars) {
c = s.charAt(i);
}
} else {
if (pos == bytes.length) {
bytes = expandBytes(bytes);
}
++i;
if(c >> 8 != 0) {
bytes[pos++] = (byte) (c >> 8);
if (pos == bytes.length) {
bytes = expandBytes(bytes);
} }
bytes[pos++] = (byte) c;
} else { } else {
if(c > 127) { bytes[pos++] = (byte) c;
//we assume this is data that is already encoded in the correct charset if (i < numChars) {
byte[] data = String.valueOf(c).getBytes(enc); c = s.charAt(i);
for(int j = 0; j < data.length; ++j) {
bytes[pos++] = data[j];
}
++i;
if (i < numChars) {
c = s.charAt(i);
}
} else {
bytes[pos++] = (byte) c;
++i;
if (i < numChars) {
c = s.charAt(i);
}
} }
} }
}


String decoded = new String(bytes, 0, pos, enc); }
buffer.append(decoded);
} catch (NumberFormatException e) {
throw UndertowMessages.MESSAGES.failedToDecodeURL(s, enc, e);
} catch (UnsupportedEncodingException e) {
throw UndertowMessages.MESSAGES.failedToDecodeURL(s, enc, e);
}
needToChange = true;
break;
default:
buffer.append(c);
i++;
if(c > 127 && !needToChange) {
needToChange = true;
} }
break;
String decoded = new String(bytes, 0, pos, enc);
buffer.append(decoded);
} catch (NumberFormatException e) {
throw UndertowMessages.MESSAGES.failedToDecodeURL(s, enc, e);
} catch (UnsupportedEncodingException e) {
throw UndertowMessages.MESSAGES.failedToDecodeURL(s, enc, e);
}
needToChange = true;
break;
} else {
buffer.append(c);
i++;
} }
} }


return (needToChange ? buffer.toString() : s); return (needToChange ? buffer.toString() : s);
} }


private static byte[] expandBytes(byte[] bytes) {
byte[] newBytes = new byte[bytes.length + 10];
System.arraycopy(bytes, 0, newBytes, 0, bytes.length);
return newBytes;
}

private abstract static class QueryStringParser { private abstract static class QueryStringParser {


void parse(final String string, final HttpServerExchange exchange, final String charset, final boolean doDecode, int max) throws ParameterLimitException { void parse(final String string, final HttpServerExchange exchange, final String charset, final boolean doDecode, int max) throws ParameterLimitException {
Expand Down
Expand Up @@ -18,7 +18,6 @@


package io.undertow.server.handlers; package io.undertow.server.handlers;


import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.Deque; import java.util.Deque;
import java.util.Iterator; import java.util.Iterator;
Expand Down Expand Up @@ -125,17 +124,15 @@ public void testQueryParametersShiftJIS() throws IOException {
@Test @Test
@ProxyIgnore @ProxyIgnore
public void testQueryParameterParsingIncorrectlyEncodedURI() throws IOException, ParameterLimitException { public void testQueryParameterParsingIncorrectlyEncodedURI() throws IOException, ParameterLimitException {
ByteArrayOutputStream out = new ByteArrayOutputStream(); StringBuilder out = new StringBuilder();
out.write(0xc7); out.append((char)0xc7);
out.write(0xd1); out.append((char)0xd1);
out.write(0x25); out.append((char)0x25);
out.write(0x32); out.append((char)0x32);
out.write(0x30); out.append((char)0x30);
out.write(0xb1); out.append((char)0xb1);
out.write(0xdb); out.append((char)0xdb);
byte[] currentString = out.toByteArray(); String s = "p=" + out.toString();
String ret = new String(currentString, 0, currentString.length, "MS949");
String s = "p=" + ret;
HttpServerExchange exchange = new HttpServerExchange(null); HttpServerExchange exchange = new HttpServerExchange(null);
URLUtils.parseQueryString(s, exchange, "MS949", true, 1000); URLUtils.parseQueryString(s, exchange, "MS949", true, 1000);
Assert.assertEquals("한 글", exchange.getQueryParameters().get("p").getFirst()); Assert.assertEquals("한 글", exchange.getQueryParameters().get("p").getFirst());
Expand Down

0 comments on commit 211cf2b

Please sign in to comment.