-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* Fixes #2619: properly handle plus signs when resolving paths Signed-off-by: Paulo Lopes <[email protected]> * Fixes #2619: properly handle plus signs when resolving paths Signed-off-by: Paulo Lopes <[email protected]> * updates based on review Signed-off-by: Paulo Lopes <[email protected]> * Delete URIDecoder.java Signed-off-by: Paulo Lopes <[email protected]> * Updates based on the review Signed-off-by: Paulo Lopes <[email protected]>
- Loading branch information
Showing
7 changed files
with
232 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
/* | ||
* Copyright (c) 2011-2017 Contributors to the Eclipse Foundation | ||
* | ||
* This program and the accompanying materials are made available under the | ||
* terms of the Eclipse Public License 2.0 which is available at | ||
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 | ||
* which is available at https://www.apache.org/licenses/LICENSE-2.0. | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 | ||
*/ | ||
|
||
package io.vertx.core.net.impl; | ||
|
||
import java.nio.charset.StandardCharsets; | ||
|
||
/** | ||
* @author <a href="mailto:[email protected]">Paulo Lopes</a> | ||
*/ | ||
public final class URIDecoder { | ||
|
||
private URIDecoder() { | ||
throw new RuntimeException("Static Class"); | ||
} | ||
|
||
/** | ||
* Decodes a segment of an URI encoded by a browser. | ||
* | ||
* The string is expected to be encoded as per RFC 3986, Section 2. This is the encoding used by JavaScript functions | ||
* encodeURI and encodeURIComponent, but not escape. For example in this encoding, é (in Unicode U+00E9 or in | ||
* UTF-8 0xC3 0xA9) is encoded as %C3%A9 or %c3%a9. | ||
* | ||
* Plus signs '+' will be handled as spaces and encoded using the default JDK URLEncoder class. | ||
* | ||
* @param s string to decode | ||
* | ||
* @return decoded string | ||
*/ | ||
public static String decodeURIComponent(String s) { | ||
return decodeURIComponent(s, true); | ||
} | ||
|
||
/** | ||
* Decodes a segment of an URI encoded by a browser. | ||
* | ||
* The string is expected to be encoded as per RFC 3986, Section 2. This is the encoding used by JavaScript functions | ||
* encodeURI and encodeURIComponent, but not escape. For example in this encoding, é (in Unicode U+00E9 or in | ||
* UTF-8 0xC3 0xA9) is encoded as %C3%A9 or %c3%a9. | ||
* | ||
* @param s string to decode | ||
* @param plus weather or not to transform plus signs into spaces | ||
* | ||
* @return decoded string | ||
*/ | ||
public static String decodeURIComponent(String s, boolean plus) { | ||
if (s == null) { | ||
return null; | ||
} | ||
|
||
final int size = s.length(); | ||
boolean modified = false; | ||
int i; | ||
for (i = 0; i < size; i++) { | ||
final char c = s.charAt(i); | ||
if (c == '%' || (plus && c == '+')) { | ||
modified = true; | ||
break; | ||
} | ||
} | ||
if (!modified) { | ||
return s; | ||
} | ||
final byte[] buf = s.getBytes(StandardCharsets.UTF_8); | ||
int pos = i; // position in `buf'. | ||
for (; i < size; i++) { | ||
char c = s.charAt(i); | ||
if (c == '%') { | ||
if (i == size - 1) { | ||
throw new IllegalArgumentException("unterminated escape" | ||
+ " sequence at end of string: " + s); | ||
} | ||
c = s.charAt(++i); | ||
if (c == '%') { | ||
buf[pos++] = '%'; // "%%" -> "%" | ||
break; | ||
} | ||
if (i >= size - 1) { | ||
throw new IllegalArgumentException("partial escape" | ||
+ " sequence at end of string: " + s); | ||
} | ||
c = decodeHexNibble(c); | ||
final char c2 = decodeHexNibble(s.charAt(++i)); | ||
if (c == Character.MAX_VALUE || c2 == Character.MAX_VALUE) { | ||
throw new IllegalArgumentException( | ||
"invalid escape sequence `%" + s.charAt(i - 1) | ||
+ s.charAt(i) + "' at index " + (i - 2) | ||
+ " of: " + s); | ||
} | ||
c = (char) (c * 16 + c2); | ||
// shouldn't check for plus since it would be a double decoding | ||
buf[pos++] = (byte) c; | ||
} else { | ||
buf[pos++] = (byte) (plus && c == '+' ? ' ' : c); | ||
} | ||
} | ||
return new String(buf, 0, pos, StandardCharsets.UTF_8); | ||
} | ||
|
||
/** | ||
* Helper to decode half of a hexadecimal number from a string. | ||
* @param c The ASCII character of the hexadecimal number to decode. | ||
* Must be in the range {@code [0-9a-fA-F]}. | ||
* @return The hexadecimal value represented in the ASCII character | ||
* given, or {@link Character#MAX_VALUE} if the character is invalid. | ||
*/ | ||
private static char decodeHexNibble(final char c) { | ||
if ('0' <= c && c <= '9') { | ||
return (char) (c - '0'); | ||
} else if ('a' <= c && c <= 'f') { | ||
return (char) (c - 'a' + 10); | ||
} else if ('A' <= c && c <= 'F') { | ||
return (char) (c - 'A' + 10); | ||
} else { | ||
return Character.MAX_VALUE; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
/* | ||
* Copyright (c) 2014 Red Hat, Inc. and others | ||
* | ||
* This program and the accompanying materials are made available under the | ||
* terms of the Eclipse Public License 2.0 which is available at | ||
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 | ||
* which is available at https://www.apache.org/licenses/LICENSE-2.0. | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 | ||
*/ | ||
|
||
package io.vertx.core.net.impl; | ||
|
||
import org.junit.Test; | ||
|
||
import java.net.URLEncoder; | ||
|
||
import static io.vertx.core.net.impl.URIDecoder.decodeURIComponent; | ||
import static org.junit.Assert.assertEquals; | ||
import static org.junit.Assert.fail; | ||
|
||
/** | ||
* @author <a href="mailto:[email protected]">Paulo Lopes</a> | ||
*/ | ||
public class URIDecoderTest { | ||
|
||
@Test | ||
public void testDecode() throws Exception { | ||
String original = "ein verr+++ückter text mit Leerzeichen, Plus und Umlauten"; | ||
String encoded = URLEncoder.encode(original, "UTF-8"); | ||
assertEquals(original, decodeURIComponent(encoded, true)); | ||
} | ||
|
||
@Test | ||
public void testPlusAsSpace() { | ||
assertEquals("foo bar", decodeURIComponent("foo+bar")); | ||
} | ||
|
||
@Test | ||
public void testPlusAsPlus() { | ||
assertEquals("foo+bar", decodeURIComponent("foo+bar", false)); | ||
} | ||
|
||
@Test | ||
public void testSpaces() { | ||
assertEquals("foo bar", decodeURIComponent("foo%20bar")); | ||
} | ||
|
||
@Test | ||
public void testSingleDecode() { | ||
assertEquals("../blah", decodeURIComponent("%2E%2E%2Fblah")); | ||
assertEquals("%20", decodeURIComponent("%2520")); | ||
} | ||
|
||
@Test | ||
public void testFromRFC() { | ||
assertEquals("/ !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~", decodeURIComponent("/%20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F%30%31%32%33%34%35%36%37%38%39%3A%3B%3C%3D%3E%3F%40%41%42%43%44%45%46%47%48%49%4A%4B%4C%4D%4E%4F%50%51%52%53%54%55%56%57%58%59%5A%5B%5C%5D%5E%5F%60%61%62%63%64%65%66%67%68%69%6A%6B%6C%6D%6E%6F%70%71%72%73%74%75%76%77%78%79%7A%7B%7C%7D%7E", false)); | ||
} | ||
|
||
@Test | ||
public void testNonLatin() { | ||
assertEquals("/foo/ñ/blah/婴儿服饰/eek/ฌ", decodeURIComponent("/foo/%C3%B1/blah/%E5%A9%B4%E5%84%BF%E6%9C%8D%E9%A5%B0/eek/%E0%B8%8C")); | ||
assertEquals("/foo/\u00F1/blah/\u5a74\u513f\u670d\u9970/eek/\u0E0C", decodeURIComponent("/foo/%C3%B1/blah/%E5%A9%B4%E5%84%BF%E6%9C%8D%E9%A5%B0/eek/%E0%B8%8C", false)); | ||
} | ||
|
||
@Test | ||
public void testIncomplete() { | ||
try { | ||
decodeURIComponent("a%"); | ||
fail("should fail"); | ||
} catch (RuntimeException e) { | ||
// expected | ||
} | ||
} | ||
|
||
@Test | ||
public void testCaseInsensitive() { | ||
assertEquals("../blah", decodeURIComponent("%2e%2e%2fblah")); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
hi! |