Repository: olingo-odata4 Updated Branches: refs/heads/master 456765b70 -> 181e46356
[OLINGO-663] Fix repository part 2 Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/181e4635 Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/181e4635 Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/181e4635 Branch: refs/heads/master Commit: 181e4635639c1f45f71c524433791b345bdd8f10 Parents: 456765b Author: Christian Amend <[email protected]> Authored: Tue Jun 2 16:21:47 2015 +0200 Committer: Christian Amend <[email protected]> Committed: Tue Jun 2 16:21:47 2015 +0200 ---------------------------------------------------------------------- .../olingo/server/api/ETagInformation.java | 74 +++++++++++ .../apache/olingo/server/core/ETagParser.java | 86 ++++++++++++ .../olingo/server/core/ETagParserTest.java | 131 +++++++++++++++++++ 3 files changed, 291 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/181e4635/lib/server-api/src/main/java/org/apache/olingo/server/api/ETagInformation.java ---------------------------------------------------------------------- diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/ETagInformation.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/ETagInformation.java new file mode 100644 index 0000000..fa65722 --- /dev/null +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/ETagInformation.java @@ -0,0 +1,74 @@ +/* + * 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.olingo.server.api; + +import java.util.Collection; + +/** + * Information about the values of an ETag-relevant HTTP header. + */ +public class ETagInformation { + private final boolean all; + private final Collection<String> eTags; + + public ETagInformation(final boolean all, final Collection<String> eTags) { + this.all = all; + this.eTags = eTags; + } + + /** + * Gets the information whether the values contain "*". + */ + public boolean isAll() { + return all; + } + + /** + * Gets the collection of ETag values found. + * It is empty if {@link #isAll()} returns <code>true</code>. + */ + public Collection<String> getETags() { + return eTags; + } + + /** + * <p>Checks whether a given ETag value is matched by this ETag information, + * using weak comparison as described in + * <a href="https://www.ietf.org/rfc/rfc7232.txt">RFC 7232</a>, section 2.3.2.</p> + * <p>If the given value is <code>null</code>, or if this ETag information + * does not contain anything, the result is <code>false</code>.</p> + * @param eTag the ETag value to match + * @return a boolean match result + */ + public boolean isMatchedBy(final String eTag) { + if (eTag == null) { + return false; + } else if (all) { + return true; + } else { + for (final String candidate : eTags) { + if ((eTag.startsWith("W/") ? eTag.substring(2) : eTag) + .equals(candidate.startsWith("W/") ? candidate.substring(2) : candidate)) { + return true; + } + } + return false; + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/181e4635/lib/server-core/src/main/java/org/apache/olingo/server/core/ETagParser.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ETagParser.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ETagParser.java new file mode 100644 index 0000000..20332f6 --- /dev/null +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ETagParser.java @@ -0,0 +1,86 @@ +/* + * 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.olingo.server.core; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * <p>Parses the values of HTTP header fields that contain a list of entity tags or a + * single star character, i.e., <code>If-Match</code> and <code>If-None-Match</code>.</p> + * <p>See <a href="https://www.ietf.org/rfc/rfc7232.txt">RFC 7232</a> for details; + * there the following grammar is defined:</p> + * <pre> + * If-Match = "*" / 1#entity-tag + * If-None-Match = "*" / 1#entity-tag + * entity-tag = [ weak ] opaque-tag + * weak = %x57.2F ; "W/", case-sensitive + * opaque-tag = DQUOTE *etagc DQUOTE + * etagc = %x21 / %x23-7E / %x80-FF + * </pre> + * <p>Values with illegal syntax do not contribute to the result but no exception is thrown.</p> + */ +public class ETagParser { + + private static final Pattern ETAG = Pattern.compile("\\s*(,\\s*)+|((?:W/)?\"[!#-~\\x80-\\xFF]*\")"); + + protected static Collection<String> parse(final Collection<String> values) { + if (values == null) { + return Collections.<String> emptySet(); + } + + Set<String> result = new HashSet<String>(); + for (final String value : values) { + final Collection<String> part = parse(value); + if (part.size() == 1 && part.iterator().next().equals("*")) { + return part; + } else { + result.addAll(part); + } + } + return result; + } + + private static Collection<String> parse(final String value) { + if (value.trim().equals("*")) { + return Collections.singleton("*"); + } else { + Set<String> result = new HashSet<String>(); + String separator = ""; + int start = 0; + Matcher matcher = ETAG.matcher(value.trim()); + while (matcher.find() && matcher.start() == start) { + start = matcher.end(); + if (matcher.group(1) != null) { + separator = matcher.group(1); + } else if (separator != null) { + result.add(matcher.group(2)); + separator = null; + } else { + return Collections.<String> emptySet(); + } + } + return matcher.hitEnd() ? result : Collections.<String> emptySet(); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/181e4635/lib/server-core/src/test/java/org/apache/olingo/server/core/ETagParserTest.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/ETagParserTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/ETagParserTest.java new file mode 100644 index 0000000..7931b2b --- /dev/null +++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/ETagParserTest.java @@ -0,0 +1,131 @@ +/* + * 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.olingo.server.core; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.hasItems; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.Collections; + +import org.apache.olingo.server.api.ETagInformation; +import org.apache.olingo.server.api.OData; +import org.junit.Test; + +public class ETagParserTest { + + private static final OData odata = OData.newInstance(); + + @Test + public void empty() { + final ETagInformation eTagInformation = odata.createETagInformation(null); + assertFalse(eTagInformation.isAll()); + assertNotNull(eTagInformation.getETags()); + assertTrue(eTagInformation.getETags().isEmpty()); + } + + @Test + public void loneStar() { + final ETagInformation eTagInformation = odata.createETagInformation(Collections.singleton("*")); + assertTrue(eTagInformation.isAll()); + assertNotNull(eTagInformation.getETags()); + assertTrue(eTagInformation.getETags().isEmpty()); + } + + @Test + public void starWins() { + final ETagInformation eTagInformation = odata.createETagInformation(Arrays.asList("\"ETag\"", "*")); + assertTrue(eTagInformation.isAll()); + assertNotNull(eTagInformation.getETags()); + assertTrue(eTagInformation.getETags().isEmpty()); + } + + @Test + public void starAsEtagAndEmptyEtag() { + final ETagInformation eTagInformation = odata.createETagInformation( + Collections.singleton("\"*\", \"\"")); + assertFalse(eTagInformation.isAll()); + assertNotNull(eTagInformation.getETags()); + assertThat(eTagInformation.getETags().size(), equalTo(2)); + assertThat(eTagInformation.getETags(), hasItems("\"*\"", "\"\"")); + } + + @Test + public void severalEtags() { + final ETagInformation eTagInformation = odata.createETagInformation( + Arrays.asList("\"ETag1\"", "\"ETag2\",, , ,W/\"ETag3\", ,")); + assertFalse(eTagInformation.isAll()); + assertNotNull(eTagInformation.getETags()); + assertThat(eTagInformation.getETags().size(), equalTo(3)); + assertThat(eTagInformation.getETags(), hasItems("\"ETag1\"", "\"ETag2\"", "W/\"ETag3\"")); + } + + @Test + public void duplicateEtagValues() { + final ETagInformation eTagInformation = odata.createETagInformation( + Arrays.asList("\"ETag1\"", "\"ETag2\", W/\"ETag1\", \"ETag1\"")); + assertFalse(eTagInformation.isAll()); + assertNotNull(eTagInformation.getETags()); + assertThat(eTagInformation.getETags().size(), equalTo(3)); + assertThat(eTagInformation.getETags(), hasItems("\"ETag1\"", "\"ETag2\"", "W/\"ETag1\"")); + } + + @Test + public void specialCharacters() { + final ETagInformation eTagInformation = odata.createETagInformation( + Collections.singleton("\"!#$%&'()*+,-./:;<=>?@[]^_`{|}~¡\u00FF\", \"ETag2\"")); + assertFalse(eTagInformation.isAll()); + assertNotNull(eTagInformation.getETags()); + assertThat(eTagInformation.getETags().size(), equalTo(2)); + assertThat(eTagInformation.getETags(), hasItems( + "\"!#$%&'()*+,-./:;<=>?@[]^_`{|}~¡\u00FF\"", "\"ETag2\"")); + } + + @Test + public void wrongFormat() { + final ETagInformation eTagInformation = odata.createETagInformation( + Arrays.asList("\"ETag1\", ETag2", "w/\"ETag3\"", "W//\"ETag4\"", "W/ETag5", + "\"\"ETag6\"\"", " \"ETag7\"\"ETag7\" ", "\"ETag8\" \"ETag8\"", + "\"ETag 9\"", "\"ETag10\"")); + assertFalse(eTagInformation.isAll()); + assertNotNull(eTagInformation.getETags()); + assertThat(eTagInformation.getETags().size(), equalTo(2)); + assertThat(eTagInformation.getETags(), hasItems("\"ETag1\"", "\"ETag10\"")); + } + + @Test + public void match() { + assertFalse(odata.createETagInformation(Collections.<String> emptySet()).isMatchedBy("\"ETag\"")); + assertFalse(odata.createETagInformation(Collections.singleton("\"ETag\"")).isMatchedBy(null)); + assertTrue(odata.createETagInformation(Collections.singleton("\"ETag\"")).isMatchedBy("\"ETag\"")); + assertTrue(odata.createETagInformation(Collections.singleton("*")).isMatchedBy("\"ETag\"")); + assertTrue(odata.createETagInformation(Collections.singleton("\"ETag\"")).isMatchedBy("W/\"ETag\"")); + assertTrue(odata.createETagInformation(Collections.singleton("W/\"ETag\"")).isMatchedBy("\"ETag\"")); + assertFalse(odata.createETagInformation(Collections.singleton("\"ETag\"")).isMatchedBy("W/\"ETag2\"")); + assertFalse(odata.createETagInformation(Collections.singleton("W/\"ETag\"")).isMatchedBy("\"ETag2\"")); + assertTrue(odata.createETagInformation(Arrays.asList("\"ETag1\",\"ETag2\"", "\"ETag3\",\"ETag4\"")) + .isMatchedBy("\"ETag4\"")); + assertFalse(odata.createETagInformation(Arrays.asList("\"ETag1\",\"ETag2\"", "\"ETag3\",\"ETag4\"")) + .isMatchedBy("\"ETag5\"")); + } +} \ No newline at end of file
