Repository: olingo-odata4 Updated Branches: refs/heads/master 75cc7197e -> eb112032d
[OLINGO-1238] Code Improvements in handling exceptions with odataVersion headers and accept headers Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/eb112032 Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/eb112032 Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/eb112032 Branch: refs/heads/master Commit: eb112032ddc0fa5264b91811d8a5ce4020326d0f Parents: 75cc719 Author: ramya vasanth <ramya.vasa...@sap.com> Authored: Mon Mar 12 14:07:22 2018 +0530 Committer: ramya vasanth <ramya.vasa...@sap.com> Committed: Mon Mar 12 14:07:22 2018 +0530 ---------------------------------------------------------------------- .../AcceptHeaderAcceptCharsetHeaderITCase.java | 336 +++++++++++++++++++ .../http/ODataVersionConformanceITCase.java | 179 ++++++++++ .../api/edm/constants/ODataServiceVersion.java | 15 + .../commons/api/format/AcceptCharset.java | 155 +++++++++ .../olingo/commons/api/format/AcceptType.java | 31 +- .../commons/api/format/AcceptCharsetTest.java | 132 ++++++++ .../commons/api/format/AcceptTypeTest.java | 93 ++++- .../AcceptHeaderContentNegotiatorException.java | 53 +++ .../olingo/server/core/ContentNegotiator.java | 73 +++- .../server/core/ContentNegotiatorException.java | 6 +- .../server/core/ODataExceptionHelper.java | 7 + .../olingo/server/core/ODataHandlerImpl.java | 20 +- .../server-core-exceptions-i18n.properties | 6 + .../server/core/ContentNegotiatorTest.java | 79 ++++- .../server/core/ODataHandlerImplTest.java | 173 ++++++++++ 15 files changed, 1327 insertions(+), 31 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/eb112032/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/AcceptHeaderAcceptCharsetHeaderITCase.java ---------------------------------------------------------------------- diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/AcceptHeaderAcceptCharsetHeaderITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/AcceptHeaderAcceptCharsetHeaderITCase.java new file mode 100644 index 0000000..b60ebb0 --- /dev/null +++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/AcceptHeaderAcceptCharsetHeaderITCase.java @@ -0,0 +1,336 @@ +/* + * 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.fit.tecsvc.http; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.net.HttpURLConnection; +import java.net.URL; + +import org.apache.commons.io.IOUtils; +import org.apache.olingo.client.api.ODataClient; +import org.apache.olingo.commons.api.format.ContentType; +import org.apache.olingo.commons.api.http.HttpHeader; +import org.apache.olingo.commons.api.http.HttpMethod; +import org.apache.olingo.commons.api.http.HttpStatusCode; +import org.apache.olingo.fit.AbstractBaseTestITCase; +import org.apache.olingo.fit.tecsvc.TecSvcConst; +import org.junit.Test; + +public class AcceptHeaderAcceptCharsetHeaderITCase extends AbstractBaseTestITCase { + + private static final String SERVICE_URI = TecSvcConst.BASE_URI + "/"; + + @Test + public void validAcceptHeader() throws Exception { + URL url = new URL(SERVICE_URI + "ESAllPrim"); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod(HttpMethod.GET.name()); + connection.setRequestProperty(HttpHeader.ACCEPT, "application/json"); + connection.connect(); + + assertEquals(HttpStatusCode.OK.getStatusCode(), connection.getResponseCode()); + + final String content = IOUtils.toString(connection.getInputStream()); + assertNotNull(content); + } + + @Test + public void invalidAcceptHeader1() throws Exception { + URL url = new URL(SERVICE_URI + "ESAllPrim"); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod(HttpMethod.GET.name()); + connection.setRequestProperty(HttpHeader.ACCEPT, "abc"); + connection.connect(); + + assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), connection.getResponseCode()); + + final String content = IOUtils.toString(connection.getErrorStream()); + assertTrue(content.contains("The content-type range 'abc' is not supported " + + "as value of the Accept header.")); + } + + @Test + public void invalidAcceptHeader2() throws Exception { + URL url = new URL(SERVICE_URI + "ESAllPrim"); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod(HttpMethod.GET.name()); + connection.setRequestProperty(HttpHeader.ACCEPT, "application/xyz"); + connection.connect(); + + assertEquals(HttpStatusCode.NOT_ACCEPTABLE.getStatusCode(), connection.getResponseCode()); + + final String content = IOUtils.toString(connection.getErrorStream()); + assertTrue(content.contains("The content-type range 'application/xyz' is " + + "not supported as value of the Accept header.")); + } + + @Test + public void validAcceptCharsetHeader() throws Exception { + URL url = new URL(SERVICE_URI + "ESAllPrim"); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod(HttpMethod.GET.name()); + connection.setRequestProperty(HttpHeader.ACCEPT_CHARSET, "utf-8;q=0.1"); + connection.setRequestProperty(HttpHeader.ACCEPT, ContentType.APPLICATION_JSON.toContentTypeString()); + connection.connect(); + + assertEquals(HttpStatusCode.OK.getStatusCode(), connection.getResponseCode()); + assertNotNull(connection.getHeaderField(HttpHeader.CONTENT_TYPE)); + ContentType contentType = ContentType.parse(connection.getHeaderField(HttpHeader.CONTENT_TYPE)); + assertEquals("application", contentType.getType()); + assertEquals("json", contentType.getSubtype()); + assertEquals(3, contentType.getParameters().size()); + assertEquals("0.1", contentType.getParameter("q")); + assertEquals("minimal", contentType.getParameter("odata.metadata")); + assertEquals("utf-8", contentType.getParameter("charset")); + + final String content = IOUtils.toString(connection.getInputStream()); + assertNotNull(content); + } + + @Test + public void invalidAcceptCharsetHeader1() throws Exception { + URL url = new URL(SERVICE_URI + "ESAllPrim"); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod(HttpMethod.GET.name()); + connection.setRequestProperty(HttpHeader.ACCEPT_CHARSET, "US-ASCII"); + connection.connect(); + + assertEquals(HttpStatusCode.NOT_ACCEPTABLE.getStatusCode(), connection.getResponseCode()); + + final String content = IOUtils.toString(connection.getErrorStream()); + assertTrue(content.contains("The charset specified in Accept charset header " + + "'US-ASCII' is not supported.")); + } + + @Test + public void invalidAcceptCharsetHeader2() throws Exception { + URL url = new URL(SERVICE_URI + "ESAllPrim"); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod(HttpMethod.GET.name()); + connection.setRequestProperty(HttpHeader.ACCEPT_CHARSET, "abc"); + connection.connect(); + + assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), connection.getResponseCode()); + + final String content = IOUtils.toString(connection.getErrorStream()); + assertTrue(content.contains("The charset specified in Accept charset header 'abc' is not supported.")); + } + + @Test + public void unsupportedAcceptHeaderWithSupportedAcceptCharsetHeader() throws Exception { + URL url = new URL(SERVICE_URI + "ESAllPrim"); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod(HttpMethod.GET.name()); + connection.setRequestProperty(HttpHeader.ACCEPT_CHARSET, "utf8;q=0.1"); + connection.setRequestProperty(HttpHeader.ACCEPT, "application/json;charset=iso8859-1"); + connection.connect(); + + assertEquals(HttpStatusCode.OK.getStatusCode(), connection.getResponseCode()); + assertNotNull(connection.getHeaderField(HttpHeader.CONTENT_TYPE)); + ContentType contentType = ContentType.parse(connection.getHeaderField(HttpHeader.CONTENT_TYPE)); + assertEquals("application", contentType.getType()); + assertEquals("json", contentType.getSubtype()); + assertEquals(3, contentType.getParameters().size()); + assertEquals("0.1", contentType.getParameter("q")); + assertEquals("minimal", contentType.getParameter("odata.metadata")); + assertEquals("utf8", contentType.getParameter("charset")); + + final String content = IOUtils.toString(connection.getInputStream()); + assertNotNull(content); + } + + @Test + public void supportedAcceptHeaderWithUnSupportedAcceptCharsetHeader() throws Exception { + URL url = new URL(SERVICE_URI + "ESAllPrim"); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod(HttpMethod.GET.name()); + connection.setRequestProperty(HttpHeader.ACCEPT_CHARSET, "iso-8859-1"); + connection.setRequestProperty(HttpHeader.ACCEPT, "application/json;charset=utf8"); + connection.connect(); + + assertEquals(HttpStatusCode.NOT_ACCEPTABLE.getStatusCode(), connection.getResponseCode()); + + final String content = IOUtils.toString(connection.getErrorStream()); + assertTrue(content.contains("The charset specified in Accept charset header " + + "'iso-8859-1' is not supported.")); + } + + @Test + public void validFormatWithAcceptCharsetHeader() throws Exception { + URL url = new URL(SERVICE_URI + "ESAllPrim?$format=json"); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod(HttpMethod.GET.name()); + connection.setRequestProperty(HttpHeader.ACCEPT_CHARSET, "utf-8;q=0.1"); + connection.connect(); + + assertEquals(HttpStatusCode.OK.getStatusCode(), connection.getResponseCode()); + assertNotNull(connection.getHeaderField(HttpHeader.CONTENT_TYPE)); + ContentType contentType = ContentType.parse(connection.getHeaderField(HttpHeader.CONTENT_TYPE)); + assertEquals("application", contentType.getType()); + assertEquals("json", contentType.getSubtype()); + assertEquals(3, contentType.getParameters().size()); + assertEquals("0.1", contentType.getParameter("q")); + assertEquals("minimal", contentType.getParameter("odata.metadata")); + assertEquals("utf-8", contentType.getParameter("charset")); + + final String content = IOUtils.toString(connection.getInputStream()); + assertNotNull(content); + } + + @Test + public void validFormatWithUnsupportedAcceptCharsetHeader() throws Exception { + URL url = new URL(SERVICE_URI + "ESAllPrim?$format=json"); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod(HttpMethod.GET.name()); + connection.setRequestProperty(HttpHeader.ACCEPT_CHARSET, "iso-8859-1"); + connection.connect(); + + assertEquals(HttpStatusCode.NOT_ACCEPTABLE.getStatusCode(), connection.getResponseCode()); + + final String content = IOUtils.toString(connection.getErrorStream()); + assertTrue(content.contains("The charset specified in Accept charset header " + + "'iso-8859-1' is not supported.")); + } + + @Test + public void validFormatWithIllegalAcceptCharsetHeader() throws Exception { + URL url = new URL(SERVICE_URI + "ESAllPrim?$format=json"); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod(HttpMethod.GET.name()); + connection.setRequestProperty(HttpHeader.ACCEPT_CHARSET, "abc"); + connection.connect(); + + assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), connection.getResponseCode()); + + final String content = IOUtils.toString(connection.getErrorStream()); + assertTrue(content.contains("The charset specified in Accept charset " + + "header 'abc' is not supported.")); + } + + @Test + public void multipleValuesInAcceptCharsetHeader1() throws Exception { + URL url = new URL(SERVICE_URI + "ESAllPrim"); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod(HttpMethod.GET.name()); + connection.setRequestProperty(HttpHeader.ACCEPT_CHARSET, "utf-8;q=0.1,iso-8859-1,unicode-1-1"); + connection.setRequestProperty(HttpHeader.ACCEPT, "application/json"); + connection.connect(); + + assertEquals(HttpStatusCode.OK.getStatusCode(), connection.getResponseCode()); + assertNotNull(connection.getHeaderField(HttpHeader.CONTENT_TYPE)); + ContentType contentType = ContentType.parse(connection.getHeaderField(HttpHeader.CONTENT_TYPE)); + assertEquals("application", contentType.getType()); + assertEquals("json", contentType.getSubtype()); + assertEquals(3, contentType.getParameters().size()); + assertEquals("minimal", contentType.getParameter("odata.metadata")); + assertEquals("utf-8", contentType.getParameter("charset")); + assertEquals("0.1", contentType.getParameter("q")); + + final String content = IOUtils.toString(connection.getInputStream()); + assertNotNull(content); + } + + @Test + public void multipleValuesInAcceptCharsetHeader2() throws Exception { + URL url = new URL(SERVICE_URI + "ESAllPrim"); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod(HttpMethod.GET.name()); + connection.setRequestProperty(HttpHeader.ACCEPT_CHARSET, "utf-8;q=0.1,utf-8;q=0.8,utf8"); + connection.setRequestProperty(HttpHeader.ACCEPT, ContentType.APPLICATION_JSON.toContentTypeString()); + connection.connect(); + + assertEquals(HttpStatusCode.OK.getStatusCode(), connection.getResponseCode()); + assertNotNull(connection.getHeaderField(HttpHeader.CONTENT_TYPE)); + ContentType contentType = ContentType.parse(connection.getHeaderField(HttpHeader.CONTENT_TYPE)); + assertEquals("application", contentType.getType()); + assertEquals("json", contentType.getSubtype()); + assertEquals(2, contentType.getParameters().size()); + assertEquals("utf8", contentType.getParameter("charset")); + assertEquals("minimal", contentType.getParameter("odata.metadata")); + + final String content = IOUtils.toString(connection.getInputStream()); + assertNotNull(content); + } + + @Test + public void multipleValuesInAcceptHeader1() throws Exception { + URL url = new URL(SERVICE_URI + "ESAllPrim"); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod(HttpMethod.GET.name()); + connection.setRequestProperty(HttpHeader.ACCEPT, "application/json," + + "application/json;q=0.1,application/json;q=0.8"); + connection.connect(); + + assertEquals(HttpStatusCode.OK.getStatusCode(), connection.getResponseCode()); + assertNotNull(connection.getHeaderField(HttpHeader.CONTENT_TYPE)); + ContentType contentType = ContentType.parse(connection.getHeaderField(HttpHeader.CONTENT_TYPE)); + assertEquals("application", contentType.getType()); + assertEquals("json", contentType.getSubtype()); + assertEquals(1, contentType.getParameters().size()); + assertEquals("minimal", contentType.getParameter("odata.metadata")); + + final String content = IOUtils.toString(connection.getInputStream()); + assertNotNull(content); + } + + @Test + public void multipleValuesInAcceptHeader2() throws Exception { + URL url = new URL(SERVICE_URI + "ESAllPrim"); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod(HttpMethod.GET.name()); + connection.setRequestProperty(HttpHeader.ACCEPT, "application/json," + + "application/json;q=0.1,application/json;q=0.8,abc"); + connection.connect(); + + assertEquals(HttpStatusCode.OK.getStatusCode(), connection.getResponseCode()); + assertNotNull(connection.getHeaderField(HttpHeader.CONTENT_TYPE)); + ContentType contentType = ContentType.parse(connection.getHeaderField(HttpHeader.CONTENT_TYPE)); + assertEquals("application", contentType.getType()); + assertEquals("json", contentType.getSubtype()); + assertEquals(1, contentType.getParameters().size()); + assertEquals("minimal", contentType.getParameter("odata.metadata")); + + final String content = IOUtils.toString(connection.getInputStream()); + assertNotNull(content); + } + + @Override + protected ODataClient getClient() { + return null; + } + +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/eb112032/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/ODataVersionConformanceITCase.java ---------------------------------------------------------------------- diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/ODataVersionConformanceITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/ODataVersionConformanceITCase.java new file mode 100644 index 0000000..a8c8912 --- /dev/null +++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/http/ODataVersionConformanceITCase.java @@ -0,0 +1,179 @@ +/* + * 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.fit.tecsvc.http; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.net.HttpURLConnection; +import java.net.URL; + +import org.apache.commons.io.IOUtils; +import org.apache.olingo.client.api.ODataClient; +import org.apache.olingo.commons.api.http.HttpHeader; +import org.apache.olingo.commons.api.http.HttpMethod; +import org.apache.olingo.commons.api.http.HttpStatusCode; +import org.apache.olingo.fit.AbstractBaseTestITCase; +import org.apache.olingo.fit.tecsvc.TecSvcConst; +import org.junit.Test; + +public class ODataVersionConformanceITCase extends AbstractBaseTestITCase { + + private static final String SERVICE_URI = TecSvcConst.BASE_URI + "/"; + + @Test + public void invalidODataVersionHeader1() throws Exception { + URL url = new URL(SERVICE_URI + "ESAllPrim"); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod(HttpMethod.GET.name()); + connection.setRequestProperty(HttpHeader.ODATA_VERSION, "3.0"); + connection.connect(); + + assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), connection.getResponseCode()); + assertEquals("4.0", connection.getHeaderField(HttpHeader.ODATA_VERSION)); + + final String content = IOUtils.toString(connection.getErrorStream()); + assertTrue(content.contains("OData version '3.0' is not supported.")); + } + + @Test + public void invalidODataVersionHeader2() throws Exception { + URL url = new URL(SERVICE_URI + "ESAllPrim"); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod(HttpMethod.GET.name()); + connection.setRequestProperty(HttpHeader.ODATA_VERSION, "5.0"); + connection.connect(); + + assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), connection.getResponseCode()); + assertEquals("4.0", connection.getHeaderField(HttpHeader.ODATA_VERSION)); + + final String content = IOUtils.toString(connection.getErrorStream()); + assertTrue(content.contains("OData version '5.0' is not supported.")); + } + + @Test + public void invalidODataMaxVersionHeader1() throws Exception { + URL url = new URL(SERVICE_URI + "ESAllPrim"); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod(HttpMethod.GET.name()); + connection.setRequestProperty(HttpHeader.ODATA_MAX_VERSION, "3.0"); + connection.connect(); + + assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), connection.getResponseCode()); + assertEquals("4.0", connection.getHeaderField(HttpHeader.ODATA_VERSION)); + + final String content = IOUtils.toString(connection.getErrorStream()); + assertTrue(content.contains("OData version '3.0' is not supported.")); + } + + @Test + public void validODataMaxVersionHeader() throws Exception { + URL url = new URL(SERVICE_URI + "ESAllPrim"); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod(HttpMethod.GET.name()); + connection.setRequestProperty(HttpHeader.ODATA_MAX_VERSION, "5.0"); + connection.connect(); + + assertEquals("4.0", connection.getHeaderField(HttpHeader.ODATA_VERSION)); + + final String content = IOUtils.toString(connection.getErrorStream()); + assertNotNull(content); + } + + @Test + public void validODataVersionAndMaxVersionHeader() throws Exception { + URL url = new URL(SERVICE_URI + "ESAllPrim"); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod(HttpMethod.GET.name()); + connection.setRequestProperty(HttpHeader.ODATA_VERSION, "4.0"); + connection.setRequestProperty(HttpHeader.ODATA_MAX_VERSION, "5.0"); + connection.connect(); + + assertEquals("4.0", connection.getHeaderField(HttpHeader.ODATA_VERSION)); + + final String content = IOUtils.toString(connection.getErrorStream()); + assertNotNull(content);; + } + + @Test + public void validODataVersionAndMaxVersionHeader1() throws Exception { + URL url = new URL(SERVICE_URI + "ESAllPrim?$format=json"); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod(HttpMethod.GET.name()); + connection.setRequestProperty(HttpHeader.ODATA_VERSION, "4.0"); + connection.setRequestProperty(HttpHeader.ODATA_MAX_VERSION, "4.01"); + connection.connect(); + + assertEquals(HttpStatusCode.OK.getStatusCode(), connection.getResponseCode()); + assertEquals("4.0", connection.getHeaderField(HttpHeader.ODATA_VERSION)); + assertEquals("application/json; odata.metadata=minimal", + connection.getHeaderField(HttpHeader.CONTENT_TYPE)); + + final String content = IOUtils.toString(connection.getInputStream()); + assertNotNull(content); + } + + @Test + public void validODataVersionAndMaxVersionHeader2() throws Exception { + URL url = new URL(SERVICE_URI + "ESAllPrim?$format=json"); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod(HttpMethod.GET.name()); + connection.setRequestProperty(HttpHeader.ODATA_VERSION, "4.0"); + connection.setRequestProperty(HttpHeader.ODATA_MAX_VERSION, "4.0"); + connection.connect(); + + assertEquals(HttpStatusCode.OK.getStatusCode(), connection.getResponseCode()); + assertEquals("4.0", connection.getHeaderField(HttpHeader.ODATA_VERSION)); + assertEquals("application/json; odata.metadata=minimal", + connection.getHeaderField(HttpHeader.CONTENT_TYPE)); + + final String content = IOUtils.toString(connection.getInputStream()); + assertNotNull(content); + } + + @Test + public void invalidODataVersionAndMaxVersionHeader() throws Exception { + URL url = new URL(SERVICE_URI + "ESAllPrim"); + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod(HttpMethod.GET.name()); + connection.setRequestProperty(HttpHeader.ODATA_VERSION, "5.0"); + connection.setRequestProperty(HttpHeader.ODATA_MAX_VERSION, "5.0"); + connection.connect(); + + assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), connection.getResponseCode()); + assertEquals("4.0", connection.getHeaderField(HttpHeader.ODATA_VERSION)); + + final String content = IOUtils.toString(connection.getErrorStream()); + assertTrue(content.contains("OData version '5.0' is not supported.")); + } + + @Override + protected ODataClient getClient() { + return null; + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/eb112032/lib/commons-api/src/main/java/org/apache/olingo/commons/api/edm/constants/ODataServiceVersion.java ---------------------------------------------------------------------- diff --git a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/edm/constants/ODataServiceVersion.java b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/edm/constants/ODataServiceVersion.java index c781191..1ef8929 100644 --- a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/edm/constants/ODataServiceVersion.java +++ b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/edm/constants/ODataServiceVersion.java @@ -87,6 +87,21 @@ public enum ODataServiceVersion { return me > other; } + public static boolean isValidODataVersion(String value) { + final double version4 = Double.parseDouble(extractDataServiceVersionString(ODataServiceVersion.V40.toString())); + final double version401 = Double.parseDouble(extractDataServiceVersionString(ODataServiceVersion.V401.toString())); + final double other = Double.parseDouble(extractDataServiceVersionString(value)); + + return (other == version4) || (other == version401); + } + + public static boolean isValidMaxODataVersion(String value) { + final double version4 = Double.parseDouble(extractDataServiceVersionString(ODataServiceVersion.V40.toString())); + final double other = Double.parseDouble(extractDataServiceVersionString(value)); + + return other >= version4; + } + /** * Extract data service version and return it. * http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/eb112032/lib/commons-api/src/main/java/org/apache/olingo/commons/api/format/AcceptCharset.java ---------------------------------------------------------------------- diff --git a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/format/AcceptCharset.java b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/format/AcceptCharset.java new file mode 100644 index 0000000..85eaf91 --- /dev/null +++ b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/format/AcceptCharset.java @@ -0,0 +1,155 @@ +/* + * 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.commons.api.format; + +import java.nio.charset.Charset; +import java.nio.charset.UnsupportedCharsetException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.regex.Pattern; + +public class AcceptCharset { + + private static final Pattern Q_PATTERN = Pattern.compile("\\A(?:0(?:\\.\\d{0,3})?)|(?:1(?:\\.0{0,3})?)\\Z"); + private final String charset; + private final Map<String, String> parameters; + private final Float quality; + + private AcceptCharset(final String charset) { + parameters = TypeUtil.createParameterMap(); + this.charset = parse(charset, parameters); + + if (TypeUtil.MEDIA_TYPE_WILDCARD.equals(this.charset)) { + throw new IllegalArgumentException("Unsupported charset in accept charset header:" + this.charset); + } else { + try { + Charset.forName(this.charset); + } catch (UnsupportedCharsetException e) { + throw new UnsupportedCharsetException("Illegal charset in accept charset header:" + this.charset); + } + if (!(this.charset.equalsIgnoreCase("utf8")) && !(this.charset.equalsIgnoreCase("utf-8"))) { + throw new IllegalArgumentException("Unsupported charset in accept charset header:" + this.charset); + } + } + final String q = parameters.get(TypeUtil.PARAMETER_Q); + if (q == null) { + quality = 1F; + } else if (Q_PATTERN.matcher(q).matches()) { + quality = Float.valueOf(q); + } else { + throw new IllegalArgumentException("Illegal quality parameter '" + q + "'."); + } + } + + private String parse(String acceptCharset, Map<String, String> parameters) { + final String[] charsetAndParameters = acceptCharset.split(TypeUtil.PARAMETER_SEPARATOR, 2); + acceptCharset = charsetAndParameters[0]; + final String params = (charsetAndParameters.length > 1 ? charsetAndParameters[1] : null); + TypeUtil.parseParameters(params, parameters); + return acceptCharset; + } + + /** + * Creates a list of {@link AcceptCharset} objects based on given input string. + * @param acceptCharsets accept types, comma-separated, as specified for the HTTP header <code>Accept-Charset</code> + * @return a list of <code>AcceptType</code> objects + * @throws Exception + * @throws IllegalArgumentException if input string is not parseable + */ + public static List<AcceptCharset> create(final String acceptCharsets) { + if (acceptCharsets == null) { + throw new IllegalArgumentException("Type parameter MUST NOT be null."); + } + List<AcceptCharset> result = new ArrayList<AcceptCharset>(); + List<Exception> exceptionList = new ArrayList<Exception>(); + + String[] values = acceptCharsets.split(","); + for (String value : values) { + try { + result.add(new AcceptCharset(value.trim())); + } catch (UnsupportedCharsetException e) { + exceptionList.add(e); + } catch (IllegalArgumentException e) { + exceptionList.add(e); + } + } + + if (result.isEmpty()) { + if (exceptionList.get(0) instanceof UnsupportedCharsetException) { + throw new UnsupportedCharsetException(exceptionList.get(0).getMessage()); + } else if (exceptionList.get(0) instanceof IllegalArgumentException) { + throw new IllegalArgumentException(exceptionList.get(0).getMessage()); + } + } + sort(result); + + return result; + } + + public String getCharset() { + return charset; + } + + public Map<String, String> getParameters() { + return Collections.unmodifiableMap(parameters); + } + + public String getParameter(final String name) { + return parameters.get(name.toLowerCase(Locale.ROOT)); + } + + public Float getQuality() { + return quality; + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + result.append(charset); + for (final Map.Entry<String, String> entry : parameters.entrySet()) { + result.append(';').append(entry.getKey()).append('=').append(entry.getValue()); + } + + return result.toString(); + } + + /** + * Sorts given list of Accept charsets + * according to their quality-parameter values and their specificity + * as defined in RFC 2616, chapters 14.2. + * @param toSort list which is sorted and hence re-arranged + */ + private static void sort(List<AcceptCharset> toSort) { + Collections.sort(toSort, + new Comparator<AcceptCharset>() { + @Override + public int compare(final AcceptCharset a1, final AcceptCharset a2) { + int compare = a2.getQuality().compareTo(a1.getQuality()); + if (compare != 0) { + return compare; + } + return a2.getParameters().size() - a1.getParameters().size(); + } + }); + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/eb112032/lib/commons-api/src/main/java/org/apache/olingo/commons/api/format/AcceptType.java ---------------------------------------------------------------------- diff --git a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/format/AcceptType.java b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/format/AcceptType.java index f864008..d2a1b24 100644 --- a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/format/AcceptType.java +++ b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/format/AcceptType.java @@ -63,9 +63,6 @@ public final class AcceptType { } private AcceptType(final String type) { - if (type == null) { - throw new IllegalArgumentException("Type parameter MUST NOT be null."); - } List<String> typeSubtype = new ArrayList<String>(); parameters = TypeUtil.createParameterMap(); @@ -119,13 +116,24 @@ public final class AcceptType { * @throws IllegalArgumentException if input string is not parseable */ public static List<AcceptType> create(final String acceptTypes) { + if (acceptTypes == null) { + throw new IllegalArgumentException("Type parameter MUST NOT be null."); + } List<AcceptType> result = new ArrayList<AcceptType>(); + List<IllegalArgumentException> exceptionList = new ArrayList<IllegalArgumentException>(); String[] values = acceptTypes.split(","); for (String value : values) { - result.add(new AcceptType(value.trim())); + try { + result.add(new AcceptType(value.trim())); + } catch (IllegalArgumentException e) { + exceptionList.add(e); + } } + if (result.isEmpty()) { + throw exceptionList.get(0); + } sort(result); return result; @@ -198,13 +206,18 @@ public final class AcceptType { } Map<String, String> compareParameters = contentType.getParameters(); for (final Map.Entry<String, String> entry : parameters.entrySet()) { - if (compareParameters.containsKey(entry.getKey()) || TypeUtil.PARAMETER_Q.equalsIgnoreCase(entry.getKey())) { - String compare = compareParameters.get(entry.getKey()); - if (!entry.getValue().equalsIgnoreCase(compare) && !TypeUtil.PARAMETER_Q.equalsIgnoreCase(entry.getKey())) { + if (entry.getKey().equalsIgnoreCase(ContentType.PARAMETER_CHARSET) && + compareParameters.containsKey(entry.getKey())) { + continue; + } else { + if (compareParameters.containsKey(entry.getKey()) || TypeUtil.PARAMETER_Q.equalsIgnoreCase(entry.getKey())) { + String compare = compareParameters.get(entry.getKey()); + if (!entry.getValue().equalsIgnoreCase(compare) && !TypeUtil.PARAMETER_Q.equalsIgnoreCase(entry.getKey())) { + return false; + } + } else { return false; } - } else { - return false; } } return true; http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/eb112032/lib/commons-api/src/test/java/org/apache/olingo/commons/api/format/AcceptCharsetTest.java ---------------------------------------------------------------------- diff --git a/lib/commons-api/src/test/java/org/apache/olingo/commons/api/format/AcceptCharsetTest.java b/lib/commons-api/src/test/java/org/apache/olingo/commons/api/format/AcceptCharsetTest.java new file mode 100644 index 0000000..a963f66 --- /dev/null +++ b/lib/commons-api/src/test/java/org/apache/olingo/commons/api/format/AcceptCharsetTest.java @@ -0,0 +1,132 @@ +/* + * 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.commons.api.format; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import java.nio.charset.UnsupportedCharsetException; +import java.util.List; + +import org.junit.Test; + +public class AcceptCharsetTest { + @Test + public void wildcard() { + expectCreateError("*"); + } + + @Test + public void illegalCharset() { + expectCreateError("abc"); + } + + @Test + public void unsupportedCharset() { + expectCreateError("iso-8859-1"); + } + + @Test + public void correctCharset() { + List<AcceptCharset> charsets = AcceptCharset.create("utf-8"); + assertEquals("utf-8", charsets.get(0).getCharset()); + } + + @Test + public void correctCharsetWithQParams() { + List<AcceptCharset> charsets = AcceptCharset.create("utf-8;q=0.8"); + assertEquals("utf-8", charsets.get(0).getCharset()); + assertEquals(1, charsets.get(0).getParameters().size()); + assertEquals("0.8", charsets.get(0).getParameter(TypeUtil.PARAMETER_Q)); + assertEquals(Float.parseFloat("0.8"), + charsets.get(0).getQuality().floatValue(), Float.parseFloat("0.8")); + } + + @Test + public void multipleCharsetsWithQParams() { + List<AcceptCharset> charsets = AcceptCharset.create("utf-8;q=0.1, utf8;q=0.8"); + assertEquals("utf8", charsets.get(0).getCharset()); + assertEquals("utf-8", charsets.get(1).getCharset()); + assertEquals(1, charsets.get(0).getParameters().size()); + assertEquals(1, charsets.get(1).getParameters().size()); + assertEquals("0.8", charsets.get(0).getParameter(TypeUtil.PARAMETER_Q)); + assertEquals("0.1", charsets.get(1).getParameter(TypeUtil.PARAMETER_Q)); + } + + @Test + public void multipleCharsetsWithQParamsAndUnsupportedCharsets() { + List<AcceptCharset> charsets = AcceptCharset.create("utf-8;q=0.1, utf8;q=0.8, iso-8859-1, abc"); + assertEquals("utf8", charsets.get(0).getCharset()); + assertEquals("utf-8", charsets.get(1).getCharset()); + assertEquals(1, charsets.get(0).getParameters().size()); + assertEquals(1, charsets.get(1).getParameters().size()); + assertEquals("0.8", charsets.get(0).getParameter(TypeUtil.PARAMETER_Q)); + assertEquals("0.1", charsets.get(1).getParameter(TypeUtil.PARAMETER_Q)); + assertEquals("utf8;q=0.8", charsets.get(0).toString()); + } + + @Test + public void multipleCharsetsWithSameQParams() { + List<AcceptCharset> charsets = AcceptCharset.create("utf-8;q=0.1, utf8;q=0.1"); + assertEquals("utf-8", charsets.get(0).getCharset()); + assertEquals("utf8", charsets.get(1).getCharset()); + assertEquals(1, charsets.get(0).getParameters().size()); + assertEquals(1, charsets.get(1).getParameters().size()); + assertEquals("0.1", charsets.get(0).getParameter(TypeUtil.PARAMETER_Q)); + assertEquals("0.1", charsets.get(1).getParameter(TypeUtil.PARAMETER_Q)); + assertEquals("utf-8;q=0.1", charsets.get(0).toString()); + } + + @Test + public void multipleCharsetsFail() { + expectCreateError("iso-8859-5, unicode-1-1;q=0.8"); + } + + private void expectCreateError(final String value) { + try { + AcceptCharset.create(value); + fail("Expected exception not thrown."); + } catch (UnsupportedCharsetException e) { + assertNotNull(e); + } catch (final IllegalArgumentException e) { + assertNotNull(e); + } + } + + @Test + public void illegalQParam() { + expectCreateError("utf-8;q=12"); + } + + @Test + public void emptyCharset() { + expectCreateError(""); + } + + @Test + public void nullCharset() { + expectCreateError(null); + } + + @Test + public void trailingSemicolon() { + expectCreateError("utf-8;"); + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/eb112032/lib/commons-api/src/test/java/org/apache/olingo/commons/api/format/AcceptTypeTest.java ---------------------------------------------------------------------- diff --git a/lib/commons-api/src/test/java/org/apache/olingo/commons/api/format/AcceptTypeTest.java b/lib/commons-api/src/test/java/org/apache/olingo/commons/api/format/AcceptTypeTest.java index 8902975..8fe0af1 100644 --- a/lib/commons-api/src/test/java/org/apache/olingo/commons/api/format/AcceptTypeTest.java +++ b/lib/commons-api/src/test/java/org/apache/olingo/commons/api/format/AcceptTypeTest.java @@ -171,4 +171,95 @@ public class AcceptTypeTest { assertNotNull(e); } } -} + + @Test + public void multipleTypeswithQParameter() { + List<AcceptType> acceptTypes = AcceptType.create("application/json;q=0.2,application/json;q=0.2"); + + assertEquals(2, acceptTypes.size()); + final AcceptType acceptType = acceptTypes.get(0); + assertEquals("application", acceptType.getType()); + assertEquals("json", acceptType.getSubtype()); + assertEquals("0.2", acceptType.getParameters().get(TypeUtil.PARAMETER_Q)); + assertEquals("0.2", acceptType.getParameter(TypeUtil.PARAMETER_Q)); + assertEquals(Float.valueOf(0.2F), acceptType.getQuality()); + assertEquals("application/json;q=0.2", acceptType.toString()); + } + + @Test + public void multipleTypeswithIllegalTypes() { + List<AcceptType> acceptTypes = AcceptType.create("application/json;q=0.2,abc"); + + assertEquals(1, acceptTypes.size()); + final AcceptType acceptType = acceptTypes.get(0); + assertEquals("application", acceptType.getType()); + assertEquals("json", acceptType.getSubtype()); + assertEquals("0.2", acceptType.getParameters().get(TypeUtil.PARAMETER_Q)); + assertEquals("0.2", acceptType.getParameter(TypeUtil.PARAMETER_Q)); + assertEquals(Float.valueOf(0.2F), acceptType.getQuality()); + assertEquals("application/json;q=0.2", acceptType.toString()); + } + + @Test + public void multipleFormatErrors() { + expectCreateError("/,abc,a/a;parameter="); + } + + @Test + public void nullAcceptType() { + expectCreateError(null); + } + + @Test + public void emptyAcceptType() { + expectCreateError(""); + } + + @Test + public void noTypeAcceptType() { + expectCreateError("/json"); + } + + @Test + public void withCharset() { + List<AcceptType> acceptTypes = AcceptType.create("application/json;charset=utf-8"); + assertEquals(1, acceptTypes.size()); + final AcceptType acceptType = acceptTypes.get(0); + assertEquals("application", acceptType.getType()); + assertEquals("json", acceptType.getSubtype()); + assertEquals("utf-8", acceptType.getParameter(ContentType.PARAMETER_CHARSET)); + + assertTrue(acceptType.matches(ContentType.create("application/json;" + + "odata.metadata=minimal;charset=utf-8"))); + assertFalse(acceptType.matches(ContentType.create("application/atom+xml;" + + "odata.metadata=minimal;charset=utf-8"))); + assertFalse(acceptType.matches(ContentType.create("application/json;" + + "odata.metadata=minimal"))); + } + + @Test + public void withSubtypeStar1() { + List<AcceptType> acceptTypes = AcceptType.create("application/json,application/*"); + assertEquals(2, acceptTypes.size()); + final AcceptType acceptType1 = acceptTypes.get(0); + assertEquals("application", acceptType1.getType()); + assertEquals("json", acceptType1.getSubtype()); + + final AcceptType acceptType2 = acceptTypes.get(1); + assertEquals("application", acceptType2.getType()); + assertEquals("*", acceptType2.getSubtype()); + } + + @Test + public void withSubtypeStar2() { + List<AcceptType> acceptTypes = AcceptType.create("application/*,application/json"); + assertEquals(2, acceptTypes.size()); + final AcceptType acceptType1 = acceptTypes.get(0); + assertEquals("application", acceptType1.getType()); + assertEquals("json", acceptType1.getSubtype()); + + final AcceptType acceptType2 = acceptTypes.get(1); + assertEquals("application", acceptType2.getType()); + assertEquals("*", acceptType2.getSubtype()); + } + } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/eb112032/lib/server-core/src/main/java/org/apache/olingo/server/core/AcceptHeaderContentNegotiatorException.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/AcceptHeaderContentNegotiatorException.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/AcceptHeaderContentNegotiatorException.java new file mode 100644 index 0000000..e051560 --- /dev/null +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/AcceptHeaderContentNegotiatorException.java @@ -0,0 +1,53 @@ +/* + * 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; + +public class AcceptHeaderContentNegotiatorException extends ContentNegotiatorException { + private static final long serialVersionUID = -8112658467394158700L; + + public static enum MessageKeys implements MessageKey { + /** parameter: list of content-type ranges */ + UNSUPPORTED_ACCEPT_TYPES, + /** parameter: format string */ + UNSUPPORTED_FORMAT_OPTION, + /** parameter: accept charset header*/ + UNSUPPORTED_ACCEPT_CHARSET_HEADER_OPTIONS; + + @Override + public String getKey() { + return name(); + } + } + + public AcceptHeaderContentNegotiatorException(final String developmentMessage, final MessageKey messageKey, + final String... parameters) { + super(developmentMessage, messageKey, parameters); + } + + public AcceptHeaderContentNegotiatorException(final String developmentMessage, final Throwable cause, + final MessageKey messageKey, + final String... parameters) { + super(developmentMessage, cause, messageKey, parameters); + } + + @Override + protected String getBundleName() { + return DEFAULT_SERVER_BUNDLE_NAME; + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/eb112032/lib/server-core/src/main/java/org/apache/olingo/server/core/ContentNegotiator.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ContentNegotiator.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ContentNegotiator.java index 4aaeee7..4ece2b8 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ContentNegotiator.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ContentNegotiator.java @@ -18,10 +18,14 @@ */ package org.apache.olingo.server.core; +import java.nio.charset.Charset; +import java.nio.charset.UnsupportedCharsetException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import org.apache.olingo.commons.api.format.AcceptCharset; import org.apache.olingo.commons.api.format.AcceptType; import org.apache.olingo.commons.api.format.ContentType; import org.apache.olingo.commons.api.http.HttpHeader; @@ -87,6 +91,23 @@ public final class ContentNegotiator { final List<ContentType> supportedContentTypes = getSupportedContentTypes(customContentTypeSupport, representationType); final String acceptHeaderValue = request.getHeader(HttpHeader.ACCEPT); + String acceptCharset = request.getHeader(HttpHeader.ACCEPT_CHARSET); + List<AcceptCharset> charsets = null; + if (acceptCharset != null) { + try { + charsets = new ArrayList<AcceptCharset>(); + charsets = AcceptCharset.create(acceptCharset); + } catch (UnsupportedCharsetException e) { + throw new AcceptHeaderContentNegotiatorException(e.getMessage(), + AcceptHeaderContentNegotiatorException.MessageKeys.UNSUPPORTED_ACCEPT_CHARSET_HEADER_OPTIONS, + e.getMessage().substring(e.getMessage().lastIndexOf(":") + 1)); + } catch (IllegalArgumentException e) { + throw new ContentNegotiatorException(e.getMessage(), + ContentNegotiatorException.MessageKeys.UNSUPPORTED_ACCEPT_CHARSET, + e.getMessage().substring(e.getMessage().lastIndexOf(":") + 1)); + } + } + ContentType result = null; if (formatOption != null && formatOption.getFormat() != null) { @@ -97,9 +118,11 @@ public final class ContentNegotiator { result = getAcceptedType( AcceptType.fromContentType(contentType == null ? ContentType.create(formatOption.getFormat()) : contentType), - supportedContentTypes); + supportedContentTypes, charsets); } catch (final IllegalArgumentException e) { - // Exception results in result = null for next check. + throw new AcceptHeaderContentNegotiatorException( + "Unsupported $format=" + formatString, + AcceptHeaderContentNegotiatorException.MessageKeys.UNSUPPORTED_FORMAT_OPTION, formatString); } if (result == null) { throw new ContentNegotiatorException("Unsupported $format = " + formatString, @@ -107,18 +130,25 @@ public final class ContentNegotiator { } } else if (acceptHeaderValue != null) { try { - result = getAcceptedType(AcceptType.create(acceptHeaderValue), supportedContentTypes); + result = getAcceptedType(AcceptType.create(acceptHeaderValue), + supportedContentTypes, charsets); } catch (final IllegalArgumentException e) { - result = null; - } + throw new AcceptHeaderContentNegotiatorException( + "Unsupported or illegal Accept header value: " + acceptHeaderValue + " != " + supportedContentTypes, + AcceptHeaderContentNegotiatorException.MessageKeys.UNSUPPORTED_ACCEPT_TYPES, acceptHeaderValue); + } if (result == null) { + List<AcceptType> types = AcceptType.create(acceptHeaderValue); throw new ContentNegotiatorException( - "Unsupported or illegal Accept header value: " + acceptHeaderValue + " != " + supportedContentTypes, + "The combination of type and subtype " + types.get(0) + + " != " + supportedContentTypes, ContentNegotiatorException.MessageKeys.UNSUPPORTED_ACCEPT_TYPES, acceptHeaderValue); } } else { final ContentType requestedContentType = getDefaultSupportedContentTypes(representationType).get(0); - result = getAcceptedType(AcceptType.fromContentType(requestedContentType), supportedContentTypes); + result = getAcceptedType(AcceptType.fromContentType(requestedContentType), + supportedContentTypes, charsets); + if (result == null) { throw new ContentNegotiatorException( "unsupported accept content type: " + requestedContentType + " != " + supportedContentTypes, @@ -145,16 +175,39 @@ public final class ContentNegotiator { } private static ContentType getAcceptedType(final List<AcceptType> acceptedContentTypes, - final List<ContentType> supportedContentTypes) { + final List<ContentType> supportedContentTypes, List<AcceptCharset> charsets) throws ContentNegotiatorException { + if (charsets != null) { + for (AcceptCharset charset : charsets) { + return getContentType(acceptedContentTypes, supportedContentTypes, charset); + } + } else { + return getContentType(acceptedContentTypes, supportedContentTypes, null); + } + return null; + } + + private static ContentType getContentType(List<AcceptType> acceptedContentTypes, + List<ContentType> supportedContentTypes, AcceptCharset charset) throws ContentNegotiatorException { for (final AcceptType acceptedType : acceptedContentTypes) { for (final ContentType supportedContentType : supportedContentTypes) { ContentType contentType = supportedContentType; final String charSetValue = acceptedType.getParameter(ContentType.PARAMETER_CHARSET); - if (charSetValue != null) { + if (charset != null) { + contentType = ContentType.create(contentType, ContentType.PARAMETER_CHARSET, charset.toString()); + } else if (charSetValue != null) { + try { + Charset.forName(charSetValue); + } catch (UnsupportedCharsetException e) { + throw new AcceptHeaderContentNegotiatorException( + "Illegal charset in Accept header: " + charSetValue, + AcceptHeaderContentNegotiatorException.MessageKeys.UNSUPPORTED_ACCEPT_CHARSET_HEADER_OPTIONS, + charSetValue); + } if ("utf8".equalsIgnoreCase(charSetValue) || "utf-8".equalsIgnoreCase(charSetValue)) { contentType = ContentType.create(contentType, ContentType.PARAMETER_CHARSET, "utf-8"); } else { - throw new IllegalArgumentException("charset not supported: " + acceptedType); + throw new ContentNegotiatorException("Unsupported accept-header-charset = " + charSetValue, + ContentNegotiatorException.MessageKeys.UNSUPPORTED_ACCEPT_HEADER_CHARSET, acceptedType.toString()); } } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/eb112032/lib/server-core/src/main/java/org/apache/olingo/server/core/ContentNegotiatorException.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ContentNegotiatorException.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ContentNegotiatorException.java index 12a0582..1b62d8f 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ContentNegotiatorException.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ContentNegotiatorException.java @@ -31,7 +31,11 @@ public class ContentNegotiatorException extends ODataLibraryException { /** no parameter */ NO_CONTENT_TYPE_SUPPORTED, /** parameter: format string */ - UNSUPPORTED_FORMAT_OPTION; + UNSUPPORTED_FORMAT_OPTION, + /**parameter: accept charset */ + UNSUPPORTED_ACCEPT_CHARSET, + /** parameter: accept header charset */ + UNSUPPORTED_ACCEPT_HEADER_CHARSET; @Override public String getKey() { http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/eb112032/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataExceptionHelper.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataExceptionHelper.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataExceptionHelper.java index 20c0634..1e0efb1 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataExceptionHelper.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataExceptionHelper.java @@ -83,6 +83,13 @@ public class ODataExceptionHelper { return serverError; } + public static ODataServerError createServerErrorObject(final AcceptHeaderContentNegotiatorException e, + final Locale requestedLocale) { + ODataServerError serverError = basicTranslatedError(e, requestedLocale); + serverError.setStatusCode(HttpStatusCode.BAD_REQUEST.getStatusCode()); + return serverError; + } + public static ODataServerError createServerErrorObject(final ODataHandlerException e, final Locale requestedLocale) { ODataServerError serverError = basicTranslatedError(e, requestedLocale); if (ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED.equals(e.getMessageKey()) http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/eb112032/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandlerImpl.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandlerImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandlerImpl.java index 0bc59be..3c18e09 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandlerImpl.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/ODataHandlerImpl.java @@ -95,6 +95,9 @@ public class ODataHandlerImpl implements ODataHandler { } catch (final UriParserException e) { ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); handleException(request, response, serverError, e); + } catch (AcceptHeaderContentNegotiatorException e) { + ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); + handleException(request, response, serverError, e); } catch (ContentNegotiatorException e) { ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e, null); handleException(request, response, serverError, e); @@ -126,6 +129,7 @@ public class ODataHandlerImpl implements ODataHandler { final int measurementHandle = debugger.startRuntimeMeasurement("ODataHandler", "processInternal"); response.setHeader(HttpHeader.ODATA_VERSION, ODataServiceVersion.V40.toString()); + try { validateODataVersion(request); } catch (final ODataHandlerException e) { @@ -180,6 +184,8 @@ public class ODataHandlerImpl implements ODataHandler { final FormatOption formatOption = getFormatOption(request, uriInfo); requestedContentType = ContentNegotiator.doContentNegotiation(formatOption, request, getCustomContentTypeSupport(), RepresentationType.ERROR); + } catch (final AcceptHeaderContentNegotiatorException e) { + requestedContentType = ContentType.JSON; } catch (final ContentNegotiatorException e) { requestedContentType = ContentType.JSON; } @@ -221,11 +227,17 @@ public class ODataHandlerImpl implements ODataHandler { } private void validateODataVersion(final ODataRequest request) throws ODataHandlerException { - final String maxVersion = request.getHeader(HttpHeader.ODATA_MAX_VERSION); - if (maxVersion != null && ODataServiceVersion.isBiggerThan(ODataServiceVersion.V40.toString(), maxVersion)) { - throw new ODataHandlerException("ODataVersion not supported: " + maxVersion, - ODataHandlerException.MessageKeys.ODATA_VERSION_NOT_SUPPORTED, maxVersion); + final String odataVersion = request.getHeader(HttpHeader.ODATA_VERSION); + if (odataVersion != null && !ODataServiceVersion.isValidODataVersion(odataVersion)) { + throw new ODataHandlerException("ODataVersion not supported: " + odataVersion, + ODataHandlerException.MessageKeys.ODATA_VERSION_NOT_SUPPORTED, odataVersion); } + + final String maxVersion = request.getHeader(HttpHeader.ODATA_MAX_VERSION); + if (maxVersion != null && !ODataServiceVersion.isValidMaxODataVersion(maxVersion)) { + throw new ODataHandlerException("ODataVersion not supported: " + maxVersion, + ODataHandlerException.MessageKeys.ODATA_VERSION_NOT_SUPPORTED, maxVersion); + } } <T extends Processor> T selectProcessor(final Class<T> cls) throws ODataHandlerException { http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/eb112032/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties b/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties index 6eb9620..19ae702 100644 --- a/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties +++ b/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties @@ -103,6 +103,12 @@ ContentNegotiatorException.UNSUPPORTED_ACCEPT_TYPES=The content-type range '%1$s ContentNegotiatorException.UNSUPPORTED_CONTENT_TYPE=The content type '%1$s' is not supported. ContentNegotiatorException.NO_CONTENT_TYPE_SUPPORTED=No content type has been specified as supported. ContentNegotiatorException.UNSUPPORTED_FORMAT_OPTION=The $format option '%1$s' is not supported. +ContentNegotiatorException.UNSUPPORTED_ACCEPT_CHARSET=The charset specified in Accept charset header '%1$s' is not supported. +ContentNegotiatorException.UNSUPPORTED_ACCEPT_HEADER_CHARSET=The charset specified in Accept header '%1$s' is not supported. + +AcceptHeaderContentNegotiatorException.UNSUPPORTED_ACCEPT_TYPES=The content-type range '%1$s' is not supported as value of the Accept header. +AcceptHeaderContentNegotiatorException.UNSUPPORTED_FORMAT_OPTION=The $format option '%1$s' is not supported. +AcceptHeaderContentNegotiatorException.UNSUPPORTED_ACCEPT_CHARSET_HEADER_OPTIONS=The charset specified in Accept charset header '%1$s' is not supported. SerializerException.NULL_METADATA_OR_EDM=The server does not define any service metadata. SerializerException.NOT_IMPLEMENTED=The requested serialization method has not been implemented yet. http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/eb112032/lib/server-core/src/test/java/org/apache/olingo/server/core/ContentNegotiatorTest.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/ContentNegotiatorTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/ContentNegotiatorTest.java index f863c70..46ec985 100644 --- a/lib/server-core/src/test/java/org/apache/olingo/server/core/ContentNegotiatorTest.java +++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/ContentNegotiatorTest.java @@ -18,9 +18,7 @@ */ package org.apache.olingo.server.core; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyListOf; import static org.mockito.Mockito.mock; @@ -42,11 +40,15 @@ public class ContentNegotiatorTest { static final private String ACCEPT_CASE_MIN = ContentType.JSON.toContentTypeString(); static final private String ACCEPT_CASE_MIN_UTF8 = "application/json;charset=UTF-8;odata.metadata=minimal"; + static final private String ACCEPT_CASE_MIN_UTF81 = "application/json;charset=utf-8;odata.metadata=minimal"; + static final private String ACCEPT_CASE_ISO_8859_1 = "application/json;charset=ISO-8859-1"; static final private String ACCEPT_CASE_FULL = ContentType.JSON_FULL_METADATA.toContentTypeString(); static final private String ACCEPT_CASE_NONE = ContentType.JSON_NO_METADATA.toContentTypeString(); static final private String ACCEPT_CASE_MIN_UTF8_IEEE754 = "application/json;charset=UTF-8;odata.metadata=minimal;IEEE754Compatible=true"; static final private String ACCEPT_CASE_MIN_IEEE754 = ACCEPT_CASE_MIN + ";IEEE754Compatible=true"; + String ACCEPT_CASE_MIN_IEEE754_1 = ACCEPT_CASE_MIN + ";IEEE754Compatible=false"; + static final private String ACCEPT_CASE_MIN_IEEE754_FAIL = ACCEPT_CASE_MIN + ";IEEE754Compatible=xyz"; static final private String ACCEPT_CASE_JSONQ = "application/json;q=0.2"; static final private String ACCEPT_CASE_XML = ContentType.APPLICATION_XML.toContentTypeString(); static final private String ACCEPT_CASE_JSON = ContentType.APPLICATION_JSON.toContentTypeString(); @@ -79,7 +81,8 @@ public class ContentNegotiatorTest { { ACCEPT_CASE_MIN_UTF8_IEEE754, null, ACCEPT_CASE_MIN_UTF8_IEEE754, null }, { ACCEPT_CASE_MIN_IEEE754, ACCEPT_CASE_MIN_IEEE754, ACCEPT_CASE_MIN , null }, { ACCEPT_CASE_XML, "xml", null, null }, - { ACCEPT_CASE_XML, null, ACCEPT_CASE_XML, null } + { ACCEPT_CASE_XML, null, ACCEPT_CASE_XML, null }, + { ACCEPT_CASE_MIN_IEEE754_1, null, ACCEPT_CASE_MIN_IEEE754_1, null } }; String[][] casesMetadata = { @@ -120,8 +123,31 @@ public class ContentNegotiatorTest { { null, null, "*", null }, { null, "a/b;charset=ISO-8859-1", null, "a/b" }, { null, null, "a/b;charset=ISO-8859-1", "a/b" }, - { null, null, null, "text/plain" } + { null, null, null, "text/plain" }, + { null, "xxx", null, null }, + { null, null, ACCEPT_CASE_MIN_IEEE754_FAIL,null } }; + + String[][] casesAcceptCharset = { + /* expected $format accept modified content types acceptCharset*/ + { ACCEPT_CASE_MIN_UTF8, null, null, null, "utf-8" }, + { ACCEPT_CASE_MIN_UTF8, "json", ACCEPT_CASE_MIN_UTF8, null, "utf-8" }, + { ACCEPT_CASE_MIN_UTF8, null, ACCEPT_CASE_ISO_8859_1, null, "utf-8" }, + { ACCEPT_CASE_MIN_UTF81, null, ACCEPT_CASE_ISO_8859_1, null, "utf-8" }, + { ACCEPT_CASE_MIN_UTF81, null, "application/json;charset=abc", null, "utf-8" }, + { ACCEPT_CASE_MIN_UTF8, null, "application/json;charset=utf-8", null, null }, + { ACCEPT_CASE_MIN_UTF8, null, "application/json;charset=utf8", null, null } + }; + + String[][] casesAcceptCharsetFail = { + /* expected $format accept modified content types acceptCharset*/ + { null, null, null, null, "ISO-8859-1"}, + { null, "json", ACCEPT_CASE_MIN_UTF8, null, "abc" }, + { null, null, ACCEPT_CASE_ISO_8859_1, null, "*" }, + { null, null, ACCEPT_CASE_ISO_8859_1, null, null }, + { null, null, "application/json;charset=abc", null, null } + }; + //CHECKSTYLE:ON //@formatter:on @@ -163,6 +189,8 @@ public class ContentNegotiatorTest { try { testContentNegotiation(useCase, RepresentationType.COLLECTION_ENTITY); fail("Exception expected for '" + useCase[1] + '|' + useCase[2] + '|' + useCase[3] + "'!"); + } catch (final AcceptHeaderContentNegotiatorException e) { + // Expected Exception } catch (final ContentNegotiatorException e) { // Expected Exception } @@ -207,7 +235,7 @@ public class ContentNegotiatorTest { } private void testContentNegotiation(final String[] useCase, final RepresentationType representationType) - throws ContentNegotiatorException { + throws Exception { FormatOption formatOption = null; if (useCase[1] != null) { @@ -219,6 +247,12 @@ public class ContentNegotiatorTest { if (useCase[2] != null) { request.addHeader(HttpHeader.ACCEPT, Arrays.asList(useCase[2])); } + + if (useCase.length > 4) { + if (useCase[4] != null) { + request.addHeader(HttpHeader.ACCEPT_CHARSET, Arrays.asList(useCase[4])); + } + } final CustomContentTypeSupport customContentTypeSupport = useCase[3] == null ? null : createCustomContentTypeSupport(useCase[3]); @@ -244,4 +278,37 @@ public class ContentNegotiatorTest { .thenReturn(types); return customContentTypeSupport; } + + @Test + public void testAcceptCharset() throws Exception { + for (String[] useCase : casesAcceptCharset) { + testContentNegotiation(useCase, RepresentationType.ENTITY); + } + } + + @Test + public void testAcceptCharsetFail() throws Exception { + for (String[] useCase : casesAcceptCharsetFail) { + try { + testContentNegotiation(useCase, RepresentationType.ENTITY); + fail("Exception expected for '" + useCase[1] + '|' + useCase[2] + '|' + useCase[3] + "'!"); + } catch (final AcceptHeaderContentNegotiatorException e) { + // Expected Exception + } catch (final ContentNegotiatorException e) { + // Expected Exception + } + } + } + + @Test + public void testSupportedTypes() throws ContentNegotiatorException, IllegalArgumentException { + assertTrue(ContentNegotiator.isSupported(ContentType.create("a/b"), + createCustomContentTypeSupport("a/b"), RepresentationType.ENTITY)); + assertFalse(ContentNegotiator.isSupported(ContentType.create("a/b"), + createCustomContentTypeSupport("x/y"), RepresentationType.ENTITY)); + assertTrue(ContentNegotiator.isSupported(ContentType.create("a/b"), + createCustomContentTypeSupport("a/b"), RepresentationType.BATCH)); + assertTrue(ContentNegotiator.isSupported(ContentType.create("a/b"), + createCustomContentTypeSupport("a/b"), RepresentationType.BINARY)); + } } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/eb112032/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerImplTest.java ---------------------------------------------------------------------- diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerImplTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerImplTest.java index bacb700..8e457b1 100644 --- a/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerImplTest.java +++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/ODataHandlerImplTest.java @@ -23,6 +23,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; @@ -33,7 +34,10 @@ import static org.mockito.Mockito.verifyZeroInteractions; import java.io.InputStream; import java.nio.charset.Charset; import java.util.Collections; +import java.util.HashMap; import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; import org.apache.commons.io.IOUtils; import org.apache.olingo.commons.api.edm.FullQualifiedName; @@ -1138,4 +1142,173 @@ public class ODataHandlerImplTest { assertEquals(statusCode.getStatusCode(), response.getStatusCode()); assertNotNull(response.getContent()); } + + @Test + public void validateInvalidOdataVersion1() throws Exception { + final String uri = "ESAllPrim(0)"; + final EntityProcessor processor = mock(EntityProcessor.class); + + final Map<String, String> header = new HashMap<String, String>(); + header.put(HttpHeader.ODATA_VERSION, "3.0"); + final ODataResponse response = dispatchToValidateHeaders + (HttpMethod.GET, uri, null, header, processor); + assertEquals("4.0", response.getHeader(HttpHeader.ODATA_VERSION)); + assertEquals(400, response.getStatusCode()); + assertNotNull(response.getContent()); + String doc = IOUtils.toString(response.getContent()); + assertTrue(doc.contains("OData version '3.0' is not supported.")); + } + + @Test + public void validateInvalidOdataVersion2() throws Exception { + final String uri = "ESAllPrim(0)"; + final EntityProcessor processor = mock(EntityProcessor.class); + + final Map<String, String> header = new HashMap<String, String>(); + header.put(HttpHeader.ODATA_VERSION, "5.0"); + + final ODataResponse response = dispatchToValidateHeaders + (HttpMethod.GET, uri, null, header, processor); + assertEquals("4.0", response.getHeader(HttpHeader.ODATA_VERSION)); + assertEquals(400, response.getStatusCode()); + assertNotNull(response.getContent()); + String doc = IOUtils.toString(response.getContent()); + assertTrue(doc.contains("OData version '5.0' is not supported.")); + } + + @Test + public void validateInvalidOdataMaxVersion1() throws Exception { + final String uri = "ESAllPrim(0)"; + final EntityProcessor processor = mock(EntityProcessor.class); + + final Map<String, String> header = new HashMap<String, String>(); + header.put(HttpHeader.ODATA_MAX_VERSION, "3.0"); + + final ODataResponse response = dispatchToValidateHeaders + (HttpMethod.GET, uri, null, header, processor); + assertEquals("4.0", response.getHeader(HttpHeader.ODATA_VERSION)); + assertEquals(400, response.getStatusCode()); + assertNotNull(response.getContent()); + String doc = IOUtils.toString(response.getContent()); + assertTrue(doc.contains("OData version '3.0' is not supported.")); + } + + @Test + public void validateValidOdataMaxVersion2() throws Exception { + final String uri = "ESAllPrim(0)"; + final EntityProcessor processor = mock(EntityProcessor.class); + + final Map<String, String> header = new HashMap<String, String>(); + header.put(HttpHeader.ODATA_MAX_VERSION, "5.0"); + + final ODataResponse response = dispatchToValidateHeaders + (HttpMethod.GET, uri, null, header, processor); + assertEquals("4.0", response.getHeader(HttpHeader.ODATA_VERSION)); + } + + @Test + public void validateValidOdataVersionAndMaxVersion1() throws Exception { + final String uri = "ESAllPrim(0)"; + final EntityProcessor processor = mock(EntityProcessor.class); + + final Map<String, String> headers = new HashMap<String, String>(); + headers.put(HttpHeader.ODATA_VERSION, "4.0"); + headers.put(HttpHeader.ODATA_MAX_VERSION, "5.0"); + + final ODataResponse response = dispatchToValidateHeaders + (HttpMethod.GET, uri, null, headers, processor); + assertEquals("4.0", response.getHeader(HttpHeader.ODATA_VERSION)); + } + + @Test + public void validateInvalidOdataVersionAndMaxVersion2() throws Exception { + final String uri = "ESAllPrim(0)"; + final EntityProcessor processor = mock(EntityProcessor.class); + + final Map<String, String> headers = new HashMap<String, String>(); + headers.put(HttpHeader.ODATA_VERSION, "3.0"); + headers.put(HttpHeader.ODATA_MAX_VERSION, "4.0"); + + final ODataResponse response = dispatchToValidateHeaders + (HttpMethod.GET, uri, null, headers, processor); + assertEquals("4.0", response.getHeader(HttpHeader.ODATA_VERSION)); + assertEquals(400, response.getStatusCode()); + assertNotNull(response.getContent()); + String doc = IOUtils.toString(response.getContent()); + assertTrue(doc.contains("OData version '3.0' is not supported.")); + } + + @Test + public void validateInvalidOdataVersionAndMaxVersion3() throws Exception { + final String uri = "ESAllPrim(0)"; + final EntityProcessor processor = mock(EntityProcessor.class); + + final Map<String, String> headers = new HashMap<String, String>(); + headers.put(HttpHeader.ODATA_VERSION, "5.0"); + headers.put(HttpHeader.ODATA_MAX_VERSION, "5.0"); + + final ODataResponse response = dispatchToValidateHeaders + (HttpMethod.GET, uri, null, headers, processor); + assertEquals("4.0", response.getHeader(HttpHeader.ODATA_VERSION)); + assertEquals(400, response.getStatusCode()); + assertNotNull(response.getContent()); + String doc = IOUtils.toString(response.getContent()); + assertTrue(doc.contains("OData version '5.0' is not supported.")); + } + + @Test + public void validateValidOdataVersionAndMaxVersion2() throws Exception { + final String uri = "ESAllPrim(0)"; + final EntityProcessor processor = mock(EntityProcessor.class); + + final Map<String, String> headers = new HashMap<String, String>(); + headers.put(HttpHeader.ODATA_VERSION, "4.0"); + headers.put(HttpHeader.ODATA_MAX_VERSION, "4.01"); + + final ODataResponse response = dispatchToValidateHeaders + (HttpMethod.GET, uri, null, headers, processor); + assertEquals("4.0", response.getHeader(HttpHeader.ODATA_VERSION)); + } + + @Test + public void validateValidOdataVersionAndMaxVersion3() throws Exception { + final String uri = "ESAllPrim(0)"; + final EntityProcessor processor = mock(EntityProcessor.class); + + final Map<String, String> headers = new HashMap<String, String>(); + headers.put(HttpHeader.ODATA_VERSION, "4.0"); + headers.put(HttpHeader.ODATA_MAX_VERSION, "4.0"); + + final ODataResponse response = dispatchToValidateHeaders + (HttpMethod.GET, uri, null, headers, processor); + assertEquals("4.0", response.getHeader(HttpHeader.ODATA_VERSION)); + } + + private ODataResponse dispatchToValidateHeaders(final HttpMethod method, final String path, final String query, + final Map<String, String> headers, final Processor processor) throws ODataHandlerException { + ODataRequest request = new ODataRequest(); + request.setMethod(method); + request.setRawBaseUri(BASE_URI); + for (Entry<String, String> header : headers.entrySet()) { + request.addHeader(header.getKey(), header.getValue()); + } + if (path.isEmpty()) { + request.setRawRequestUri(BASE_URI); + } + request.setRawODataPath(path); + request.setRawQueryPath(query); + + final OData odata = OData.newInstance(); + final ServiceMetadata metadata = odata.createServiceMetadata( + new EdmTechProvider(), Collections.<EdmxReference> emptyList()); + + ODataHandlerImpl handler = new ODataHandlerImpl(odata, metadata, new ServerCoreDebugger(odata)); + + if (processor != null) { + handler.register(processor); + } + + final ODataResponse response = handler.process(request); + return response; + } }