| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF 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 org.apache.tomcat.util.http; |
| |
| import java.nio.charset.StandardCharsets; |
| |
| import javax.servlet.http.Cookie; |
| |
| import org.junit.Assert; |
| import org.junit.Test; |
| |
| import org.apache.tomcat.util.buf.MessageBytes; |
| |
| public class TestCookies { |
| private final Cookie FOO = new Cookie("foo", "bar"); |
| private final Cookie FOO_EMPTY = new Cookie("foo", ""); |
| private final Cookie FOO_CONTROL = new Cookie("foo", "b\u00e1r"); |
| private final Cookie BAR = new Cookie("bar", "rab"); |
| private final Cookie BAR_EMPTY = new Cookie("bar", ""); |
| private final Cookie A = new Cookie("a", "b"); |
| private final Cookie HASH_EMPTY = new Cookie("#", ""); |
| private final Cookie $PORT = new Cookie("$Port", "8080"); |
| |
| @Test |
| public void testBasicCookieOld() { |
| doTestBasicCookie(false); |
| } |
| |
| @Test |
| public void testBasicCookieRfc6265() { |
| doTestBasicCookie(true); |
| } |
| |
| private void doTestBasicCookie(boolean useRfc6265) { |
| test(useRfc6265, "foo=bar; a=b", FOO, A); |
| test(useRfc6265, "foo=bar;a=b", FOO, A); |
| test(useRfc6265, "foo=bar;a=b;", FOO, A); |
| test(useRfc6265, "foo=bar;a=b; ", FOO, A); |
| test(useRfc6265, "foo=bar;a=b; ;", FOO, A); |
| } |
| |
| @Test |
| public void testNameOnlyAreDroppedOld() { |
| test(false, "foo=;a=b; ;", A); |
| test(false, "foo;a=b; ;", A); |
| test(false, "foo;a=b;bar", A); |
| test(false, "foo;a=b;bar;", A); |
| test(false, "foo;a=b;bar ", A); |
| test(false, "foo;a=b;bar ;", A); |
| |
| // Bug 49000 |
| Cookie fred = new Cookie("fred", "1"); |
| Cookie jim = new Cookie("jim", "2"); |
| Cookie george = new Cookie("george", "3"); |
| test(false, "fred=1; jim=2; bob", fred, jim); |
| test(false, "fred=1; jim=2; bob; george=3", fred, jim, george); |
| test(false, "fred=1; jim=2; bob=; george=3", fred, jim, george); |
| test(false, "fred=1; jim=2; bob=", fred, jim); |
| } |
| |
| @Test |
| public void testNameOnlyAreDroppedRfc6265() { |
| // Name only cookies are not dropped in RFC6265 |
| test(true, "foo=;a=b; ;", FOO_EMPTY, A); |
| test(true, "foo;a=b; ;", FOO_EMPTY, A); |
| test(true, "foo;a=b;bar", FOO_EMPTY, A, BAR_EMPTY); |
| test(true, "foo;a=b;bar;", FOO_EMPTY, A, BAR_EMPTY); |
| test(true, "foo;a=b;bar ", FOO_EMPTY, A, BAR_EMPTY); |
| test(true, "foo;a=b;bar ;", FOO_EMPTY, A, BAR_EMPTY); |
| |
| // Bug 49000 |
| Cookie fred = new Cookie("fred", "1"); |
| Cookie jim = new Cookie("jim", "2"); |
| Cookie bobEmpty = new Cookie("bob", ""); |
| Cookie george = new Cookie("george", "3"); |
| test(true, "fred=1; jim=2; bob", fred, jim, bobEmpty); |
| test(true, "fred=1; jim=2; bob; george=3", fred, jim, bobEmpty, george); |
| test(true, "fred=1; jim=2; bob=; george=3", fred, jim, bobEmpty, george); |
| test(true, "fred=1; jim=2; bob=", fred, jim, bobEmpty); |
| } |
| |
| @Test |
| public void testQuotedValueOld() { |
| doTestQuotedValue(false); |
| } |
| |
| @Test |
| public void testQuotedValueRfc6265() { |
| doTestQuotedValue(true); |
| } |
| |
| private void doTestQuotedValue(boolean useRfc6265) { |
| test(useRfc6265, "foo=bar;a=\"b\"", FOO, A); |
| test(useRfc6265, "foo=bar;a=\"b\";", FOO, A); |
| } |
| |
| @Test |
| public void testEmptyPairsOld() { |
| test(false, "foo;a=b; ;bar", A); |
| test(false, "foo;a=b;;bar", A); |
| test(false, "foo;a=b; ;;bar=rab", A, BAR); |
| test(false, "foo;a=b;; ;bar=rab", A, BAR); |
| test(false, "foo;a=b;;#;bar=rab", A, BAR); |
| test(false, "foo;a=b;;\\;bar=rab", A, BAR); |
| } |
| |
| @Test |
| public void testEmptyPairsRfc6265() { |
| test(true, "foo;a=b; ;bar", FOO_EMPTY, A, BAR_EMPTY); |
| test(true, "foo;a=b;;bar", FOO_EMPTY, A, BAR_EMPTY); |
| test(true, "foo;a=b; ;;bar=rab", FOO_EMPTY, A, BAR); |
| test(true, "foo;a=b;; ;bar=rab", FOO_EMPTY, A, BAR); |
| test(true, "foo;a=b;;#;bar=rab", FOO_EMPTY, A, HASH_EMPTY, BAR); |
| test(true, "foo;a=b;;\\;bar=rab", FOO_EMPTY, A, BAR); |
| } |
| |
| @Test |
| public void testSeparatorsInValueOld() { |
| doTestSeparatorsInValue(false); |
| } |
| |
| @Test |
| public void testSeparatorsInValueRfc6265() { |
| doTestSeparatorsInValue(true); |
| } |
| |
| private void doTestSeparatorsInValue(boolean useRfc6265) { |
| test(useRfc6265, "a=()<>@:\\\"/[]?={}\t; foo=bar", FOO); |
| } |
| |
| |
| @Test |
| public void v1TokenValueOld() { |
| doV1TokenValue(false); |
| } |
| |
| @Test |
| public void v1TokenValueRfc6265() { |
| doV1TokenValue(true); |
| } |
| |
| private void doV1TokenValue(boolean useRfc6265) { |
| FOO.setVersion(1); |
| A.setVersion(1); |
| test(useRfc6265, "$Version=1; foo=bar;a=b", FOO, A); |
| test(useRfc6265, "$Version=1;foo=bar;a=b; ; ", FOO, A); |
| } |
| |
| @Test |
| public void v1NameOnlyIsDroppedOld() { |
| doV1NameOnlyIsDropped(false); |
| } |
| |
| @Test |
| public void v1NameOnlyIsDroppedRfc6265() { |
| doV1NameOnlyIsDropped(true); |
| } |
| |
| private void doV1NameOnlyIsDropped(boolean useRfc6265) { |
| A.setVersion(1); |
| test(useRfc6265, "$Version=1;foo=;a=b; ; ", A); |
| test(useRfc6265, "$Version=1;foo= ;a=b; ; ", A); |
| test(useRfc6265, "$Version=1;foo;a=b; ; ", A); |
| } |
| |
| @Test |
| public void v1QuotedValueOld() { |
| doV1QuotedValue(false); |
| } |
| |
| @Test |
| public void v1QuotedValueRfc6265() { |
| doV1QuotedValue(true); |
| } |
| |
| private void doV1QuotedValue(boolean useRfc6265) { |
| FOO.setVersion(1); |
| A.setVersion(1); |
| // presumes quotes are removed |
| test(useRfc6265, "$Version=1;foo=\"bar\";a=b; ; ", FOO, A); |
| } |
| |
| @Test |
| public void v1DQuoteInValueOld() { |
| FOO.setValue("b"); |
| FOO.setVersion(1); |
| A.setVersion(1); |
| test(false, "$Version=1;foo=\"b\"ar\";a=b", FOO, A); // Incorrectly escaped. |
| } |
| |
| @Test |
| public void v1DQuoteInValueRfc6265() { |
| A.setVersion(1); |
| test(true, "$Version=1;foo=\"b\"ar\";a=b", A); // Incorrectly escaped. |
| } |
| |
| @Test |
| public void v1QuoteInValueOld() { |
| doV1QuoteInValue(false); |
| } |
| |
| @Test |
| public void v1QuoteInValueRfc6265() { |
| doV1QuoteInValue(true); |
| } |
| |
| private void doV1QuoteInValue(boolean useRfc6265) { |
| FOO.setValue("b'ar"); |
| FOO.setVersion(1); |
| A.setVersion(1); |
| test(useRfc6265, "$Version=1;foo=b'ar;a=b", FOO, A); |
| } |
| |
| |
| @Test |
| public void v1QuoteInQuotedValueOld() { |
| doV1QuoteInQuotedValue(false); |
| } |
| |
| @Test |
| public void v1QuoteInQuotedValueRfc6265() { |
| doV1QuoteInQuotedValue(true); |
| } |
| |
| private void doV1QuoteInQuotedValue(boolean useRfc6265) { |
| FOO.setValue("b'ar"); |
| FOO.setVersion(1); |
| A.setVersion(1); |
| test(useRfc6265, "$Version=1;foo=\"b'ar\";a=b", FOO, A); |
| } |
| |
| @Test |
| public void v1EscapedDQuoteInValueOld() { |
| doV1EscapedDQuoteInValue(false); |
| } |
| |
| @Test |
| public void v1EscapedDQuoteInValueRfc6265() { |
| doV1EscapedDQuoteInValue(true); |
| } |
| |
| private void doV1EscapedDQuoteInValue(boolean useRfc6265) { |
| FOO.setValue("b\"ar"); |
| FOO.setVersion(1); |
| A.setVersion(1); |
| test(useRfc6265, "$Version=1;foo=\"b\\\"ar\";a=b", FOO, A); // correctly escaped. |
| } |
| |
| @Test |
| public void v1QuotedValueEndsInBackslashOld() { |
| doV1QuotedValueEndsInBackslash(false); |
| } |
| |
| @Test |
| public void v1QuotedValueEndsInBackslashRfc6265() { |
| doV1QuotedValueEndsInBackslash(true); |
| } |
| |
| private void doV1QuotedValueEndsInBackslash(boolean useRfc6265) { |
| FOO.setVersion(1); |
| test(useRfc6265, "$Version=1;foo=bar;a=\"b\\\"", FOO); |
| } |
| |
| @Test |
| public void v1MismatchedQuotesOld() { |
| doV1MismatchedQuotes(false); |
| } |
| |
| @Test |
| public void v1MismatchedQuotesRfc6265() { |
| doV1MismatchedQuotes(true); |
| } |
| |
| private void doV1MismatchedQuotes(boolean useRfc6265) { |
| FOO.setVersion(1); |
| test(useRfc6265, "$Version=1;foo=bar;a=\"b\\", FOO); |
| } |
| |
| @Test |
| public void v1SingleQuotesAreValidTokenCharactersOld() { |
| doV1SingleQuotesAreValidTokenCharacters(false); |
| } |
| |
| @Test |
| public void v1SingleQuotesAreValidTokenCharactersRfc6265() { |
| doV1SingleQuotesAreValidTokenCharacters(true); |
| } |
| |
| private void doV1SingleQuotesAreValidTokenCharacters(boolean useRfc6265) { |
| FOO.setVersion(1); |
| FOO.setValue("'bar'"); |
| test(useRfc6265, "$Version=1; foo='bar'", FOO); |
| } |
| |
| @Test |
| public void v1DomainIsParsedOld() { |
| doV1DomainIsParsed(false); |
| } |
| |
| @Test |
| public void v1DomainIsParsedRfc6265() { |
| doV1DomainIsParsed(true); |
| } |
| |
| private void doV1DomainIsParsed(boolean useRfc6265) { |
| FOO.setVersion(1); |
| FOO.setDomain("apache.org"); |
| A.setVersion(1); |
| A.setDomain("yahoo.com"); |
| test(useRfc6265, "$Version=1;foo=\"bar\";$Domain=apache.org;a=b;$Domain=yahoo.com", FOO, A); |
| } |
| |
| @Test |
| public void v1DomainOnlyAffectsPrecedingCookieOld() { |
| doV1DomainOnlyAffectsPrecedingCookie(false); |
| } |
| |
| @Test |
| public void v1DomainOnlyAffectsPrecedingCookieRfc6265() { |
| doV1DomainOnlyAffectsPrecedingCookie(true); |
| } |
| |
| private void doV1DomainOnlyAffectsPrecedingCookie(boolean useRfc6265) { |
| FOO.setVersion(1); |
| FOO.setDomain("apache.org"); |
| A.setVersion(1); |
| test(useRfc6265, "$Version=1;foo=\"bar\";$Domain=apache.org;a=b", FOO, A); |
| } |
| |
| @Test |
| public void v1PortIsIgnoredOld() { |
| FOO.setVersion(1); |
| FOO.setDomain("apache.org"); |
| A.setVersion(1); |
| test(false, "$Version=1;foo=\"bar\";$Domain=apache.org;$Port=8080;a=b", FOO, A); |
| } |
| |
| @Test |
| public void v1PortIsIgnoredRfc6265() { |
| FOO.setVersion(1); |
| FOO.setDomain("apache.org"); |
| $PORT.setVersion(1); |
| A.setVersion(1); |
| test(true, "$Version=1;foo=\"bar\";$Domain=apache.org;$Port=8080;a=b", FOO, $PORT, A); |
| } |
| |
| @Test |
| public void v1PathAffectsPrecedingCookieOld() { |
| doV1PathAffectsPrecedingCookie(false); |
| } |
| |
| @Test |
| public void v1PathAffectsPrecedingCookieRfc6265() { |
| doV1PathAffectsPrecedingCookie(true); |
| } |
| |
| private void doV1PathAffectsPrecedingCookie(boolean useRfc6265) { |
| FOO.setVersion(1); |
| FOO.setPath("/examples"); |
| A.setVersion(1); |
| test(useRfc6265, "$Version=1;foo=\"bar\";$Path=/examples;a=b; ; ", FOO, A); |
| } |
| |
| @Test |
| public void rfc2109Version0Old() { |
| // rfc2109 semantically does not allow $Version to be 0 but it is valid syntax |
| test(false, "$Version=0;foo=bar", FOO); |
| } |
| |
| @Test |
| public void rfc2109Version0Rfc6265() { |
| // Neither RFC2109 nor RFc6265 allow version 0 |
| test(true, "$Version=0;foo=bar"); |
| } |
| |
| @Test |
| public void disallow8bitInName() { |
| // Bug 55917 |
| test(true, "f\u00f6o=bar"); |
| } |
| |
| @Test |
| public void disallowControlInName() { |
| // Bug 55917 |
| test(true, "f\010o=bar"); |
| } |
| |
| @Test |
| public void disallow8BitControlInName() { |
| // Bug 55917 |
| test(true, "f\210o=bar"); |
| } |
| |
| @Test |
| public void allow8BitInV0Value() { |
| // Bug 55917 |
| test(true, "foo=b\u00e1r", FOO_CONTROL); |
| } |
| |
| @Test |
| public void disallow8bitInV1UnquotedValue() { |
| // Bug 55917 |
| test(true, "$Version=1; foo=b\u00e1r"); |
| } |
| |
| @Test |
| public void allow8bitInV1QuotedValue() { |
| // Bug 55917 |
| FOO_CONTROL.setVersion(1); |
| test(true, "$Version=1; foo=\"b\u00e1r\"", FOO_CONTROL); |
| } |
| |
| @Test |
| public void disallowControlInV0Value() { |
| // Bug 55917 |
| test(true, "foo=b\010r"); |
| } |
| |
| @Test |
| public void disallowControlInV1UnquotedValue() { |
| // Bug 55917 |
| test(true, "$Version=1; foo=b\010r"); |
| } |
| |
| @Test |
| public void disallowControlInV1QuotedValue() { |
| // Bug 55917 / Bug 55918 |
| test(true, "$Version=1; foo=\"b\010r\""); |
| } |
| |
| @Test |
| public void disallow8BitControlInV1UnquotedValue() { |
| // Bug 55917 |
| test(true, "$Version=1; foo=b\210r"); |
| } |
| |
| @Test |
| public void testJsonInV0() { |
| // Bug 55921 |
| test(true, "{\"a\":true, \"b\":false};a=b", A); |
| } |
| |
| @Test |
| public void testJsonInV1() { |
| // Bug 55921 |
| A.setVersion(1); |
| test(true, "$Version=1;{\"a\":true, \"b\":false};a=b", A); |
| } |
| |
| @Test |
| public void testSkipSemicolonOrComma() { |
| // V1 cookies can also use commas to separate cookies |
| FOO.setVersion(1); |
| A.setVersion(1); |
| test(true, "$Version=1;x\tx=yyy,foo=bar;a=b", FOO, A); |
| } |
| |
| private void test(boolean useRfc6265, String header, Cookie... expected) { |
| MimeHeaders mimeHeaders = new MimeHeaders(); |
| ServerCookies serverCookies = new ServerCookies(4); |
| CookieProcessor cookieProcessor; |
| |
| if (useRfc6265) { |
| cookieProcessor = new Rfc6265CookieProcessor(); |
| } else { |
| cookieProcessor = new LegacyCookieProcessor(); |
| } |
| MessageBytes cookieHeaderValue = mimeHeaders.addValue("Cookie"); |
| byte[] bytes = header.getBytes(StandardCharsets.UTF_8); |
| cookieHeaderValue.setBytes(bytes, 0, bytes.length); |
| cookieProcessor.parseCookieHeader(mimeHeaders, serverCookies); |
| Assert.assertEquals(expected.length, serverCookies.getCookieCount()); |
| for (int i = 0; i < expected.length; i++) { |
| Cookie cookie = expected[i]; |
| ServerCookie actual = serverCookies.getCookie(i); |
| Assert.assertEquals(cookie.getVersion(), actual.getVersion()); |
| Assert.assertEquals(cookie.getName(), actual.getName().toString()); |
| actual.getValue().getByteChunk().setCharset(StandardCharsets.UTF_8); |
| Assert.assertEquals(cookie.getValue(), |
| org.apache.tomcat.util.http.parser.Cookie.unescapeCookieValueRfc2109( |
| actual.getValue().toString())); |
| if (cookie.getVersion() == 1) { |
| Assert.assertEquals(cookie.getDomain(), actual.getDomain().toString()); |
| Assert.assertEquals(cookie.getPath(), actual.getPath().toString()); |
| } |
| } |
| } |
| } |