/* * Copyright 2012 The Netty Project * * The Netty Project licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package io.netty.handler.codec.http; import io.netty.util.CharsetUtil; import org.junit.Assert; import org.junit.Test; import java.net.URI; import java.net.URISyntaxException; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; public class QueryStringDecoderTest { @Test public void testBasicUris() throws URISyntaxException { QueryStringDecoder d = new QueryStringDecoder(new URI("http://localhost/path")); Assert.assertEquals(0, d.parameters().size()); } @Test public void testBasic() throws Exception { QueryStringDecoder d; d = new QueryStringDecoder("/foo?a=b=c"); Assert.assertEquals("/foo", d.path()); Assert.assertEquals(1, d.parameters().size()); Assert.assertEquals(1, d.parameters().get("a").size()); Assert.assertEquals("b=c", d.parameters().get("a").get(0)); d = new QueryStringDecoder("/foo?a=1&a=2"); Assert.assertEquals("/foo", d.path()); Assert.assertEquals(1, d.parameters().size()); Assert.assertEquals(2, d.parameters().get("a").size()); Assert.assertEquals("1", d.parameters().get("a").get(0)); Assert.assertEquals("2", d.parameters().get("a").get(1)); d = new QueryStringDecoder("/foo?a=&a=2"); Assert.assertEquals("/foo", d.path()); Assert.assertEquals(1, d.parameters().size()); Assert.assertEquals(2, d.parameters().get("a").size()); Assert.assertEquals("", d.parameters().get("a").get(0)); Assert.assertEquals("2", d.parameters().get("a").get(1)); d = new QueryStringDecoder("/foo?a=1&a="); Assert.assertEquals("/foo", d.path()); Assert.assertEquals(1, d.parameters().size()); Assert.assertEquals(2, d.parameters().get("a").size()); Assert.assertEquals("1", d.parameters().get("a").get(0)); Assert.assertEquals("", d.parameters().get("a").get(1)); d = new QueryStringDecoder("/foo?a=1&a=&a="); Assert.assertEquals("/foo", d.path()); Assert.assertEquals(1, d.parameters().size()); Assert.assertEquals(3, d.parameters().get("a").size()); Assert.assertEquals("1", d.parameters().get("a").get(0)); Assert.assertEquals("", d.parameters().get("a").get(1)); Assert.assertEquals("", d.parameters().get("a").get(2)); d = new QueryStringDecoder("/foo?a=1=&a==2"); Assert.assertEquals("/foo", d.path()); Assert.assertEquals(1, d.parameters().size()); Assert.assertEquals(2, d.parameters().get("a").size()); Assert.assertEquals("1=", d.parameters().get("a").get(0)); Assert.assertEquals("=2", d.parameters().get("a").get(1)); } @Test public void testExotic() throws Exception { assertQueryString("", ""); assertQueryString("foo", "foo"); assertQueryString("/foo", "/foo"); assertQueryString("?a=", "?a"); assertQueryString("foo?a=", "foo?a"); assertQueryString("/foo?a=", "/foo?a"); assertQueryString("/foo?a=", "/foo?a&"); assertQueryString("/foo?a=", "/foo?&a"); assertQueryString("/foo?a=", "/foo?&a&"); assertQueryString("/foo?a=", "/foo?&=a"); assertQueryString("/foo?a=", "/foo?=a&"); assertQueryString("/foo?a=", "/foo?a=&"); assertQueryString("/foo?a=b&c=d", "/foo?a=b&&c=d"); assertQueryString("/foo?a=b&c=d", "/foo?a=b&=&c=d"); assertQueryString("/foo?a=b&c=d", "/foo?a=b&==&c=d"); assertQueryString("/foo?a=b&c=&x=y", "/foo?a=b&c&x=y"); assertQueryString("/foo?a=", "/foo?a="); assertQueryString("/foo?a=", "/foo?&a="); assertQueryString("/foo?a=b&c=d", "/foo?a=b&c=d"); assertQueryString("/foo?a=1&a=&a=", "/foo?a=1&a&a="); } @Test public void testHashDos() throws Exception { StringBuilder buf = new StringBuilder(); buf.append('?'); for (int i = 0; i < 65536; i ++) { buf.append('k'); buf.append(i); buf.append("=v"); buf.append(i); buf.append('&'); } Assert.assertEquals(1024, new QueryStringDecoder(buf.toString()).parameters().size()); } @Test public void testHasPath() throws Exception { QueryStringDecoder decoder = new QueryStringDecoder("1=2", false); Assert.assertEquals("", decoder.path()); Map<String, List<String>> params = decoder.parameters(); Assert.assertEquals(1, params.size()); Assert.assertTrue(params.containsKey("1")); List<String> param = params.get("1"); Assert.assertNotNull(param); Assert.assertEquals(1, param.size()); Assert.assertEquals("2", param.get(0)); } @Test public void testUrlDecoding() throws Exception { final String caffe = new String( // "CaffĂ©" but instead of putting the literal E-acute in the // source file, we directly use the UTF-8 encoding so as to // not rely on the platform's default encoding (not portable). new byte[] {'C', 'a', 'f', 'f', (byte) 0xC3, (byte) 0xA9}, "UTF-8"); final String[] tests = { // Encoded -> Decoded or error message substring "", "", "foo", "foo", "f%%b", "f%b", "f+o", "f o", "f++", "f ", "fo%", "unterminated escape sequence", "%42", "B", "%5f", "_", "f%4", "partial escape sequence", "%x2", "invalid escape sequence `%x2' at index 0 of: %x2", "%4x", "invalid escape sequence `%4x' at index 0 of: %4x", "Caff%C3%A9", caffe, }; for (int i = 0; i < tests.length; i += 2) { final String encoded = tests[i]; final String expected = tests[i + 1]; try { final String decoded = QueryStringDecoder.decodeComponent(encoded); Assert.assertEquals(expected, decoded); } catch (IllegalArgumentException e) { Assert.assertTrue("String \"" + e.getMessage() + "\" does" + " not contain \"" + expected + '"', e.getMessage().contains(expected)); } } } private static void assertQueryString(String expected, String actual) { QueryStringDecoder ed = new QueryStringDecoder(expected, CharsetUtil.UTF_8); QueryStringDecoder ad = new QueryStringDecoder(actual, CharsetUtil.UTF_8); Assert.assertEquals(ed.path(), ad.path()); Assert.assertEquals(ed.parameters(), ad.parameters()); } // See #189 @Test public void testURI() { URI uri = URI.create("http://localhost:8080/foo?param1=value1¶m2=value2¶m3=value3"); QueryStringDecoder decoder = new QueryStringDecoder(uri); Assert.assertEquals("/foo", decoder.path()); Map<String, List<String>> params = decoder.parameters(); Assert.assertEquals(3, params.size()); Iterator<Entry<String, List<String>>> entries = params.entrySet().iterator(); Entry<String, List<String>> entry = entries.next(); Assert.assertEquals("param1", entry.getKey()); Assert.assertEquals(1, entry.getValue().size()); Assert.assertEquals("value1", entry.getValue().get(0)); entry = entries.next(); Assert.assertEquals("param2", entry.getKey()); Assert.assertEquals(1, entry.getValue().size()); Assert.assertEquals("value2", entry.getValue().get(0)); entry = entries.next(); Assert.assertEquals("param3", entry.getKey()); Assert.assertEquals(1, entry.getValue().size()); Assert.assertEquals("value3", entry.getValue().get(0)); Assert.assertFalse(entries.hasNext()); } // See #189 @Test public void testURISlashPath() { URI uri = URI.create("http://localhost:8080/?param1=value1¶m2=value2¶m3=value3"); QueryStringDecoder decoder = new QueryStringDecoder(uri); Assert.assertEquals("/", decoder.path()); Map<String, List<String>> params = decoder.parameters(); Assert.assertEquals(3, params.size()); Iterator<Entry<String, List<String>>> entries = params.entrySet().iterator(); Entry<String, List<String>> entry = entries.next(); Assert.assertEquals("param1", entry.getKey()); Assert.assertEquals(1, entry.getValue().size()); Assert.assertEquals("value1", entry.getValue().get(0)); entry = entries.next(); Assert.assertEquals("param2", entry.getKey()); Assert.assertEquals(1, entry.getValue().size()); Assert.assertEquals("value2", entry.getValue().get(0)); entry = entries.next(); Assert.assertEquals("param3", entry.getKey()); Assert.assertEquals(1, entry.getValue().size()); Assert.assertEquals("value3", entry.getValue().get(0)); Assert.assertFalse(entries.hasNext()); } // See #189 @Test public void testURINoPath() { URI uri = URI.create("http://localhost:8080?param1=value1¶m2=value2¶m3=value3"); QueryStringDecoder decoder = new QueryStringDecoder(uri); Assert.assertEquals("", decoder.path()); Map<String, List<String>> params = decoder.parameters(); Assert.assertEquals(3, params.size()); Iterator<Entry<String, List<String>>> entries = params.entrySet().iterator(); Entry<String, List<String>> entry = entries.next(); Assert.assertEquals("param1", entry.getKey()); Assert.assertEquals(1, entry.getValue().size()); Assert.assertEquals("value1", entry.getValue().get(0)); entry = entries.next(); Assert.assertEquals("param2", entry.getKey()); Assert.assertEquals(1, entry.getValue().size()); Assert.assertEquals("value2", entry.getValue().get(0)); entry = entries.next(); Assert.assertEquals("param3", entry.getKey()); Assert.assertEquals(1, entry.getValue().size()); Assert.assertEquals("value3", entry.getValue().get(0)); Assert.assertFalse(entries.hasNext()); } // See https://github.com/netty/netty/issues/1833 @Test public void testURI2() { URI uri = URI.create("http://foo.com/images;num=10?query=name;value=123"); QueryStringDecoder decoder = new QueryStringDecoder(uri); Assert.assertEquals("/images;num=10", decoder.path()); Map<String, List<String>> params = decoder.parameters(); Assert.assertEquals(2, params.size()); Iterator<Entry<String, List<String>>> entries = params.entrySet().iterator(); Entry<String, List<String>> entry = entries.next(); Assert.assertEquals("query", entry.getKey()); Assert.assertEquals(1, entry.getValue().size()); Assert.assertEquals("name", entry.getValue().get(0)); entry = entries.next(); Assert.assertEquals("value", entry.getKey()); Assert.assertEquals(1, entry.getValue().size()); Assert.assertEquals("123", entry.getValue().get(0)); Assert.assertFalse(entries.hasNext()); } }