diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/backend/page/PageState.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/backend/page/PageState.java index cfd75f6bc4..a6e2aa44a4 100644 --- a/hugegraph-core/src/main/java/com/baidu/hugegraph/backend/page/PageState.java +++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/backend/page/PageState.java @@ -29,6 +29,8 @@ public class PageState { public static final byte[] EMPTY_BYTES = new byte[0]; public static final PageState EMPTY = new PageState(EMPTY_BYTES, 0, 0); + public static final char SPACE = ' '; + public static final char PLUS = '+'; private final byte[] position; private final int offset; @@ -73,6 +75,12 @@ private byte[] toBytes() { public static PageState fromString(String page) { E.checkNotNull(page, "page"); + /* + * URLDecoder will auto decode '+' to space in url due to the request + * of HTML4, so we choose to replace the space to '+' after getting it + * More details refer to #1437 + */ + page = page.replace(SPACE, PLUS); return fromBytes(toBytes(page)); } diff --git a/hugegraph-test/src/main/java/com/baidu/hugegraph/core/VertexCoreTest.java b/hugegraph-test/src/main/java/com/baidu/hugegraph/core/VertexCoreTest.java index df73ef0c82..3355650105 100644 --- a/hugegraph-test/src/main/java/com/baidu/hugegraph/core/VertexCoreTest.java +++ b/hugegraph-test/src/main/java/com/baidu/hugegraph/core/VertexCoreTest.java @@ -21,6 +21,7 @@ import java.math.BigDecimal; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashSet; @@ -54,6 +55,7 @@ import com.baidu.hugegraph.backend.id.SnowflakeIdGenerator; import com.baidu.hugegraph.backend.id.SplicingIdGenerator; import com.baidu.hugegraph.backend.page.PageInfo; +import com.baidu.hugegraph.backend.page.PageState; import com.baidu.hugegraph.backend.query.Condition; import com.baidu.hugegraph.backend.query.ConditionQuery; import com.baidu.hugegraph.backend.query.Query; @@ -6667,6 +6669,35 @@ public void testQueryByPageWithInvalidPage() { }); } + @Test + public void testQueryByPageWithSpecialBase64Chars() { + Assume.assumeTrue("Not support paging", + storeFeatures().supportsQueryByPage()); + final String pageWith3Base64Chars = "AAAAADsyABwAEAqI546LS6WW57unBgA" + + "EAAAAAPB////+8H////4alhxAZS8va6" + + "opcAKpklipAAQAAAAAAAAAAQ=="; + + final String pageWithSpace = "AAAAADsyABwAEAqI546LS6WW57unBgAEAAAAAP" + + "B//// 8H////4alhxAZS8va6opcAKpklipAAQA" + + "AAAAAAAAAQ=="; + + HugeGraph graph = graph(); + init100Books(); + + // Contains valid character '+' and '/' and '=' + GraphTraversal traversal; + traversal = graph.traversal().V() + .has("~page", pageWith3Base64Chars).limit(10); + Assert.assertNotNull(traversal); + CloseableIterator.closeIterator(traversal); + + // Contains invalid base64 character ' ', will be replaced to '+' + traversal = graph.traversal().V() + .has("~page", pageWithSpace).limit(10); + Assert.assertNotNull(traversal); + CloseableIterator.closeIterator(traversal); + } + @Test public void testQueryByPageWithInvalidLimit() { Assume.assumeTrue("Not support paging", diff --git a/hugegraph-test/src/main/java/com/baidu/hugegraph/unit/UnitTestSuite.java b/hugegraph-test/src/main/java/com/baidu/hugegraph/unit/UnitTestSuite.java index 1de772539a..e1e8673a6b 100644 --- a/hugegraph-test/src/main/java/com/baidu/hugegraph/unit/UnitTestSuite.java +++ b/hugegraph-test/src/main/java/com/baidu/hugegraph/unit/UnitTestSuite.java @@ -37,6 +37,7 @@ import com.baidu.hugegraph.unit.core.DirectionsTest; import com.baidu.hugegraph.unit.core.ExceptionTest; import com.baidu.hugegraph.unit.core.LocksTableTest; +import com.baidu.hugegraph.unit.core.PageStateTest; import com.baidu.hugegraph.unit.core.QueryTest; import com.baidu.hugegraph.unit.core.RangeTest; import com.baidu.hugegraph.unit.core.RolePermissionTest; @@ -101,6 +102,7 @@ ExceptionTest.class, BackendStoreSystemInfoTest.class, TraversalUtilTest.class, + PageStateTest.class, /* serializer */ BytesBufferTest.class, diff --git a/hugegraph-test/src/main/java/com/baidu/hugegraph/unit/core/PageStateTest.java b/hugegraph-test/src/main/java/com/baidu/hugegraph/unit/core/PageStateTest.java new file mode 100644 index 0000000000..6fd1860b62 --- /dev/null +++ b/hugegraph-test/src/main/java/com/baidu/hugegraph/unit/core/PageStateTest.java @@ -0,0 +1,90 @@ +/* + * Copyright 2017 HugeGraph Authors + * + * 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 com.baidu.hugegraph.unit.core; + +import java.util.Arrays; + +import org.junit.Test; + +import com.baidu.hugegraph.backend.BackendException; +import com.baidu.hugegraph.backend.page.PageState; +import com.baidu.hugegraph.testutil.Assert; + +public class PageStateTest { + + private String pageWith3Base64Chars = "AAAAADsyABwAEAqI546LS6WW57unBgA" + + "EAAAAAPB////+8H////4alhxAZS8va6" + + "opcAKpklipAAQAAAAAAAAAAQ=="; + + private String pageWithSpace = "AAAAADsyABwAEAqI546LS6WW57unBgAEAAAAAP" + + "B//// 8H////4alhxAZS8va6opcAKpklipAAQA" + + "AAAAAAAAAQ=="; + + @Test + public void testOriginalStringPageToBytes() { + byte[] validPage = PageState.toBytes(pageWith3Base64Chars); + Assert.assertNotNull(validPage); + + Assert.assertThrows(BackendException.class, () -> { + PageState.toBytes(pageWithSpace); + }, e -> { + Assert.assertContains("Invalid page:", e.toString()); + }); + } + + @Test + public void testDecodePageWithSpecialBase64Chars() { + // Assert decode '+' and '/' and '=' and space successfully + Assert.assertNotNull(PageState.fromString(pageWith3Base64Chars)); + + byte[] decodePlus = PageState.fromString(pageWith3Base64Chars) + .position(); + byte[] decodeSpace = PageState.fromString(pageWithSpace).position(); + + Assert.assertTrue(Arrays.equals(decodePlus, decodeSpace)); + } + + @Test + public void testDecodePageWithInvalidStringPage() { + final String invalidPageWithBase64Chars = "dGVzdCBiYXNlNjQ="; + + Assert.assertThrows(BackendException.class, () -> { + PageState.fromString(invalidPageWithBase64Chars); + }, e -> { + Assert.assertContains("Invalid page: '0x", e.toString()); + }); + + final String invalidBase64Chars = "!abc~"; + Assert.assertThrows(BackendException.class, () -> { + PageState.fromString(invalidBase64Chars); + }, e -> { + Assert.assertContains("Invalid page:", e.toString()); + }); + } + + @Test + public void testEmptyPageState() { + Assert.assertEquals(0, PageState.EMPTY.offset()); + Assert.assertNull(PageState.EMPTY.toString()); + + Assert.assertEquals(PageState.EMPTY, + PageState.fromBytes(PageState.EMPTY_BYTES)); + } +}