[OLINGO-1003] server batch handling corrections - binary content in bodies - absolute-path URIs - HTTP headers with allowed non-letter characters
Signed-off-by: Christian Amend <[email protected]> Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/14278ac1 Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/14278ac1 Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/14278ac1 Branch: refs/heads/master Commit: 14278ac189aad2513632b7f4ec519b2197717469 Parents: 20a0070 Author: Klaus Straubinger <[email protected]> Authored: Thu Sep 15 13:59:50 2016 +0200 Committer: Christian Amend <[email protected]> Committed: Thu Sep 15 14:09:34 2016 +0200 ---------------------------------------------------------------------- .../fit/tecsvc/client/BatchClientITCase.java | 346 +- .../batch/BatchDeserializerException.java | 4 +- .../deserializer/batch/BatchLineReader.java | 46 +- .../deserializer/batch/BatchParserCommon.java | 15 +- .../batch/BatchRequestTransformator.java | 64 +- .../batch/BatchTransformatorCommon.java | 35 +- .../batch/HttpRequestStatusLine.java | 13 +- .../server-core-exceptions-i18n.properties | 2 +- .../deserializer/batch/BatchLineReaderTest.java | 100 +- .../batch/BatchParserCommonTest.java | 66 + .../batch/BatchRequestParserTest.java | 1401 ++- .../batch/HttpRequestStatusLineTest.java | 63 +- .../serializer/BatchResponseSerializerTest.java | 308 +- .../src/test/resources/batchLarge.batch | 8857 +++++++++++++----- .../src/test/resources/batchWithPost.batch | 39 - 15 files changed, 7866 insertions(+), 3493 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/14278ac1/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BatchClientITCase.java ---------------------------------------------------------------------- diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BatchClientITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BatchClientITCase.java index 913d9f9..a03d3c7 100644 --- a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BatchClientITCase.java +++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BatchClientITCase.java @@ -23,11 +23,12 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import java.io.ByteArrayInputStream; import java.net.URI; -import java.net.URISyntaxException; import java.util.Iterator; import org.apache.olingo.client.api.ODataClient; +import org.apache.olingo.client.api.communication.ODataClientErrorException; import org.apache.olingo.client.api.communication.request.batch.BatchManager; import org.apache.olingo.client.api.communication.request.batch.ODataBatchRequest; import org.apache.olingo.client.api.communication.request.batch.ODataBatchResponseItem; @@ -37,6 +38,8 @@ import org.apache.olingo.client.api.communication.request.cud.ODataEntityUpdateR import org.apache.olingo.client.api.communication.request.cud.UpdateType; import org.apache.olingo.client.api.communication.request.retrieve.ODataEntityRequest; import org.apache.olingo.client.api.communication.request.retrieve.ODataEntitySetRequest; +import org.apache.olingo.client.api.communication.request.retrieve.ODataPropertyRequest; +import org.apache.olingo.client.api.communication.request.streamed.ODataMediaEntityCreateRequest; import org.apache.olingo.client.api.communication.response.ODataBatchResponse; import org.apache.olingo.client.api.communication.response.ODataEntityCreateResponse; import org.apache.olingo.client.api.communication.response.ODataEntityUpdateResponse; @@ -45,10 +48,8 @@ import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse import org.apache.olingo.client.api.domain.ClientEntity; import org.apache.olingo.client.api.domain.ClientEntitySet; import org.apache.olingo.client.api.domain.ClientObjectFactory; +import org.apache.olingo.client.api.domain.ClientProperty; import org.apache.olingo.client.api.http.HttpClientException; -import org.apache.olingo.client.api.uri.URIBuilder; -import org.apache.olingo.client.core.communication.request.batch.ODataChangesetResponseItem; -import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; import org.apache.olingo.commons.api.edm.FullQualifiedName; import org.apache.olingo.commons.api.format.ContentType; import org.apache.olingo.commons.api.format.PreferenceName; @@ -90,11 +91,9 @@ public class BatchClientITCase extends AbstractParamTecSvcITCase { // Try to create entity, with invalid type ClientObjectFactory factory = getFactory(); final ClientEntity entity = factory.newEntity(ES_NOT_AVAILABLE); - entity.getProperties().add(factory.newPrimitiveProperty(PROPERTY_STRING, factory - .newPrimitiveValueBuilder() - .buildString("1"))); - final ODataBatchRequest batchRequest = getClient().getBatchRequestFactory().getBatchRequest(SERVICE_URI); - final BatchManager payloadManager = batchRequest.payloadManager(); + entity.getProperties().add( + factory.newPrimitiveProperty(PROPERTY_STRING, factory.newPrimitiveValueBuilder().buildString("1"))); + BatchManager payloadManager = getClient().getBatchRequestFactory().getBatchRequest(SERVICE_URI).payloadManager(); final ODataChangeset changeset = payloadManager.addChangeset(); final URI targetURI = getClient().newURIBuilder(SERVICE_URI) .appendEntitySetSegment(ES_NOT_AVAILABLE_NAME) @@ -124,10 +123,10 @@ public class BatchClientITCase extends AbstractParamTecSvcITCase { @Test public void emptyBatchRequest() { // create your request - final ODataBatchRequest request = getClient().getBatchRequestFactory().getBatchRequest(SERVICE_URI); - - final BatchManager payload = request.payloadManager(); - final ODataBatchResponse response = payload.getResponse(); + ODataBatchRequest request = getClient().getBatchRequestFactory().getBatchRequest(SERVICE_URI); + setCookieHeader(request); + final ODataBatchResponse response = request.payloadManager().getResponse(); + saveCookieHeader(response); assertEquals(HttpStatusCode.ACCEPTED.getStatusCode(), response.getStatusCode()); assertEquals("Accepted", response.getStatusMessage()); @@ -137,16 +136,15 @@ public class BatchClientITCase extends AbstractParamTecSvcITCase { } @Test - public void getBatchRequestWithRelativeUris() throws URISyntaxException { - final ODataBatchRequest request = getClient().getBatchRequestFactory().getBatchRequest(SERVICE_URI); - - final BatchManager payload = request.payloadManager(); - - // create new request - appendGetRequest(payload, "ESAllPrim", 32767, true); + public void getBatchRequestWithRelativeUris() { + ODataBatchRequest request = getClient().getBatchRequestFactory().getBatchRequest(SERVICE_URI); + setCookieHeader(request); + BatchManager payload = request.payloadManager(); + payload.addRequest(createGetRequest("ESAllPrim", 32767, true)); // Fetch result final ODataBatchResponse response = payload.getResponse(); + saveCookieHeader(response); assertEquals(HttpStatusCode.ACCEPTED.getStatusCode(), response.getStatusCode()); assertEquals("Accepted", response.getStatusMessage()); @@ -160,24 +158,25 @@ public class BatchClientITCase extends AbstractParamTecSvcITCase { ODataResponse oDataResponse = item.next(); assertNotNull(oDataResponse); assertEquals(HttpStatusCode.OK.getStatusCode(), oDataResponse.getStatusCode()); - assertEquals(1, oDataResponse.getHeader("OData-Version").size()); - assertEquals("4.0", oDataResponse.getHeader("OData-Version").toArray()[0]); - assertEquals(1, oDataResponse.getHeader("Content-Length").size()); - assertEquals("605", oDataResponse.getHeader("Content-Length").toArray()[0]); - assertEquals(ContentType.JSON.toContentTypeString(), oDataResponse.getContentType()); + assertEquals(1, oDataResponse.getHeader(HttpHeader.ODATA_VERSION).size()); + assertEquals("4.0", oDataResponse.getHeader(HttpHeader.ODATA_VERSION).iterator().next()); + assertEquals(1, oDataResponse.getHeader(HttpHeader.CONTENT_LENGTH).size()); + assertEquals(isJson() ? "605" : "2246", oDataResponse.getHeader(HttpHeader.CONTENT_LENGTH).iterator().next()); + assertContentType(oDataResponse.getContentType()); } @Test - public void getBatchRequest() throws URISyntaxException { - final ODataBatchRequest request = getClient().getBatchRequestFactory().getBatchRequest(SERVICE_URI); - - final BatchManager payload = request.payloadManager(); + public void getBatchRequest() { + ODataBatchRequest request = getClient().getBatchRequestFactory().getBatchRequest(SERVICE_URI); + setCookieHeader(request); + BatchManager payload = request.payloadManager(); // create new request - appendGetRequest(payload, "ESAllPrim", 32767, false); + payload.addRequest(createGetRequest("ESAllPrim", 32767, false)); // Fetch result final ODataBatchResponse response = payload.getResponse(); + saveCookieHeader(response); assertEquals(HttpStatusCode.ACCEPTED.getStatusCode(), response.getStatusCode()); assertEquals("Accepted", response.getStatusMessage()); @@ -191,25 +190,93 @@ public class BatchClientITCase extends AbstractParamTecSvcITCase { ODataResponse oDataResponse = item.next(); assertNotNull(oDataResponse); assertEquals(HttpStatusCode.OK.getStatusCode(), oDataResponse.getStatusCode()); - assertEquals(1, oDataResponse.getHeader("OData-Version").size()); - assertEquals("4.0", oDataResponse.getHeader("OData-Version").toArray()[0]); - assertEquals(1, oDataResponse.getHeader("Content-Length").size()); - assertEquals("605", oDataResponse.getHeader("Content-Length").toArray()[0]); - assertEquals(ContentType.JSON.toContentTypeString(), oDataResponse.getContentType()); + assertEquals(1, oDataResponse.getHeader(HttpHeader.ODATA_VERSION).size()); + assertEquals("4.0", oDataResponse.getHeader(HttpHeader.ODATA_VERSION).iterator().next()); + assertEquals(1, oDataResponse.getHeader(HttpHeader.CONTENT_LENGTH).size()); + assertEquals(isJson() ? "605" : "2246", oDataResponse.getHeader(HttpHeader.CONTENT_LENGTH).iterator().next()); + assertContentType(oDataResponse.getContentType()); } @Test - public void errorWithoutContinueOnErrorPreferHeader() throws URISyntaxException { - final ODataBatchRequest request = getClient().getBatchRequestFactory().getBatchRequest(SERVICE_URI); + public void binaryContent() throws Exception { + BatchManager payload = getClient().getBatchRequestFactory().getBatchRequest(SERVICE_URI).payloadManager(); + ODataMediaEntityCreateRequest<ClientEntity> request = getClient().getCUDRequestFactory() + .getMediaEntityCreateRequest( + getClient().newURIBuilder(SERVICE_URI).appendEntitySetSegment("ESMedia").build(), + new ByteArrayInputStream(new byte[] { -42, 0, 1 })); + request.setContentType(ContentType.APPLICATION_OCTET_STREAM.toContentTypeString()); + request.addCustomHeader("Custom-Header-0123456789", "!!!"); + payload.addRequest(request); + + payload.addRequest(getClient().getRetrieveRequestFactory().getMediaRequest(URI.create( + "ESMedia(5)/$value"))); - final BatchManager payload = request.payloadManager(); + final ODataBatchResponse response = payload.getResponse(); + assertEquals(HttpStatusCode.ACCEPTED.getStatusCode(), response.getStatusCode()); - appendGetRequest(payload, "ESAllPrim", 32767, false); // Without error - appendGetRequest(payload, "ESAllPrim", 42, false); // Error ( Key does not exist ) - appendGetRequest(payload, "ESAllPrim", 0, false); // Without error + Iterator<ODataBatchResponseItem> iter = response.getBody(); + assertTrue(iter.hasNext()); + ODataBatchResponseItem item = iter.next(); + assertFalse(item.isChangeset()); + + ODataResponse oDataResponse = item.next(); + assertNotNull(oDataResponse); + assertEquals(HttpStatusCode.CREATED.getStatusCode(), oDataResponse.getStatusCode()); + assertFalse(item.hasNext()); + + assertTrue(iter.hasNext()); + oDataResponse = iter.next().next(); + assertEquals(HttpStatusCode.OK.getStatusCode(), oDataResponse.getStatusCode()); + assertEquals(ContentType.APPLICATION_OCTET_STREAM.toContentTypeString(), oDataResponse.getContentType()); + // TODO: Correct the client code so that the following content verification can be enabled. +// InputStream rawResponse = oDataResponse.getRawResponse(); +// assertEquals(-42, (byte) rawResponse.read()); +// assertEquals(0, rawResponse.read()); +// assertEquals(1, rawResponse.read()); +// assertEquals(-1, rawResponse.read()); + assertFalse(iter.hasNext()); + } + + @Test + public void absolutePath() { + BatchManager payload = getClient().getBatchRequestFactory().getBatchRequest(SERVICE_URI).payloadManager(); + final URI uri = URI.create( + getClient().newURIBuilder(SERVICE_URI).appendEntitySetSegment("ESTwoPrim").appendKeySegment(32767).build() + .getRawPath()); + ODataPropertyRequest<ClientProperty> request = getClient().getRetrieveRequestFactory() + .getPropertyRequest(uri); + setCookieHeader(request); + request.addCustomHeader("Custom-Header-0123456789", "!!!"); + payload.addRequest(request); + + final ODataBatchResponse response = payload.getResponse(); + saveCookieHeader(response); + assertEquals(HttpStatusCode.ACCEPTED.getStatusCode(), response.getStatusCode()); + + Iterator<ODataBatchResponseItem> iter = response.getBody(); + assertTrue(iter.hasNext()); + ODataBatchResponseItem item = iter.next(); + assertFalse(item.isChangeset()); + + ODataResponse oDataResponse = item.next(); + assertNotNull(oDataResponse); + assertEquals(HttpStatusCode.OK.getStatusCode(), oDataResponse.getStatusCode()); + assertFalse(item.hasNext()); + assertFalse(iter.hasNext()); + } + + @Test + public void errorWithoutContinueOnErrorPreferHeader() { + ODataBatchRequest request = getClient().getBatchRequestFactory().getBatchRequest(SERVICE_URI); + setCookieHeader(request); + BatchManager payload = request.payloadManager(); + payload.addRequest(createGetRequest("ESAllPrim", 32767, false)); // Without error + payload.addRequest(createGetRequest("ESAllPrim", 42, false)); // Error ( Key does not exist ) + payload.addRequest(createGetRequest("ESAllPrim", 0, false)); // Without error // Fetch result final ODataBatchResponse response = payload.getResponse(); + saveCookieHeader(response); assertEquals(HttpStatusCode.ACCEPTED.getStatusCode(), response.getStatusCode()); final Iterator<ODataBatchResponseItem> iter = response.getBody(); @@ -222,11 +289,11 @@ public class BatchClientITCase extends AbstractParamTecSvcITCase { ODataResponse oDataResponse = item.next(); assertNotNull(oDataResponse); assertEquals(HttpStatusCode.OK.getStatusCode(), oDataResponse.getStatusCode()); - assertEquals(1, oDataResponse.getHeader("OData-Version").size()); - assertEquals("4.0", oDataResponse.getHeader("OData-Version").toArray()[0]); - assertEquals(1, oDataResponse.getHeader("Content-Length").size()); - assertEquals("605", oDataResponse.getHeader("Content-Length").toArray()[0]); - assertEquals(ContentType.JSON.toContentTypeString(), oDataResponse.getContentType()); + assertEquals(1, oDataResponse.getHeader(HttpHeader.ODATA_VERSION).size()); + assertEquals("4.0", oDataResponse.getHeader(HttpHeader.ODATA_VERSION).iterator().next()); + assertEquals(1, oDataResponse.getHeader(HttpHeader.CONTENT_LENGTH).size()); + assertEquals(isJson() ? "605" : "2246", oDataResponse.getHeader(HttpHeader.CONTENT_LENGTH).iterator().next()); + assertContentType(oDataResponse.getContentType()); // Check second get request assertTrue(iter.hasNext()); @@ -242,17 +309,18 @@ public class BatchClientITCase extends AbstractParamTecSvcITCase { } @Test - public void invalidAbsoluteUri() throws URISyntaxException { - final ODataBatchRequest request = getClient().getBatchRequestFactory().getBatchRequest(SERVICE_URI); - - final BatchManager payload = request.payloadManager(); - final URI uri = new URI(SERVICE_URI + "../ESAllPrim(32767)"); - final ODataEntityRequest<ClientEntity> queryReq = getClient().getRetrieveRequestFactory().getEntityRequest(uri); - queryReq.setFormat(ContentType.JSON); + public void invalidAbsoluteUri() { + ODataBatchRequest request = getClient().getBatchRequestFactory().getBatchRequest(SERVICE_URI); + setCookieHeader(request); + BatchManager payload = request.payloadManager(); + ODataEntityRequest<ClientEntity> queryReq = getClient().getRetrieveRequestFactory().getEntityRequest(URI.create( + SERVICE_URI + "../ESAllPrim(32767)")); + queryReq.setFormat(getContentType()); payload.addRequest(queryReq); // Fetch result final ODataBatchResponse response = payload.getResponse(); + saveCookieHeader(response); assertEquals(HttpStatusCode.ACCEPTED.getStatusCode(), response.getStatusCode()); final Iterator<ODataBatchResponseItem> bodyIterator = response.getBody(); @@ -265,48 +333,54 @@ public class BatchClientITCase extends AbstractParamTecSvcITCase { assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), oDataResponse.getStatusCode()); } - @Test(expected = HttpClientException.class) - public void invalidHost() throws URISyntaxException { - final ODataBatchRequest request = getClient().getBatchRequestFactory().getBatchRequest(SERVICE_URI); - - final BatchManager payload = request.payloadManager(); - final URI uri = new URI("http://otherhost/odata/ESAllPrim(32767)"); - final ODataEntityRequest<ClientEntity> queryReq = getClient().getRetrieveRequestFactory().getEntityRequest(uri); - queryReq.setFormat(ContentType.JSON); - payload.addRequest(queryReq); - - // Fetch result - payload.getResponse(); + @Test + public void invalidHost() { + ODataBatchRequest request = getClient().getBatchRequestFactory().getBatchRequest(SERVICE_URI); + setCookieHeader(request); + BatchManager payload = request.payloadManager(); + payload.addRequest(getClient().getRetrieveRequestFactory().getEntityRequest(URI.create( + "http://otherhost/odata/ESAllPrim(32767)"))); + + try { + payload.getResponse(); + } catch (final HttpClientException e) { + assertTrue(e.getCause().getCause() instanceof ODataClientErrorException); + assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), + ((ODataClientErrorException) e.getCause().getCause()).getStatusLine().getStatusCode()); + } } - @Test(expected = HttpClientException.class) - public void invalidAbsoluteRequest() throws URISyntaxException { - final ODataBatchRequest request = getClient().getBatchRequestFactory().getBatchRequest(SERVICE_URI); - - final BatchManager payload = request.payloadManager(); - final URI uri = new URI("/ESAllPrim(32767)"); - final ODataEntityRequest<ClientEntity> queryReq = getClient().getRetrieveRequestFactory().getEntityRequest(uri); - queryReq.setFormat(ContentType.JSON); - payload.addRequest(queryReq); - - // Fetch result - payload.getResponse(); + @Test + public void invalidAbsoluteRequest() { + ODataBatchRequest request = getClient().getBatchRequestFactory().getBatchRequest(SERVICE_URI); + setCookieHeader(request); + BatchManager payload = request.payloadManager(); + payload.addRequest(getClient().getRetrieveRequestFactory().getEntityRequest(URI.create( + "/ESAllPrim(32767)"))); + + try { + payload.getResponse(); + } catch (final HttpClientException e) { + assertTrue(e.getCause().getCause() instanceof ODataClientErrorException); + assertEquals(HttpStatusCode.BAD_REQUEST.getStatusCode(), + ((ODataClientErrorException) e.getCause().getCause()).getStatusLine().getStatusCode()); + } } @Test - public void errorWithContinueOnErrorPreferHeader() throws Exception { + public void errorWithContinueOnErrorPreferHeader() { ODataClient client = getClient(); client.getConfiguration().setContinueOnError(true); - final ODataBatchRequest request = client.getBatchRequestFactory().getBatchRequest(SERVICE_URI); - - final BatchManager payload = request.payloadManager(); - - appendGetRequest(payload, "ESAllPrim", 32767, false); // Without error - appendGetRequest(payload, "ESAllPrim", 42, false); // Error ( Key does not exist ) - appendGetRequest(payload, "ESAllPrim", 0, false); // Without error + ODataBatchRequest request = client.getBatchRequestFactory().getBatchRequest(SERVICE_URI); + setCookieHeader(request); + BatchManager payload = request.payloadManager(); + payload.addRequest(createGetRequest("ESAllPrim", 32767, false)); // Without error + payload.addRequest(createGetRequest("ESAllPrim", 42, false)); // Error ( Key does not exist ) + payload.addRequest(createGetRequest("ESAllPrim", 0, false)); // Without error // Fetch result final ODataBatchResponse response = payload.getResponse(); + saveCookieHeader(response); assertEquals(HttpStatusCode.ACCEPTED.getStatusCode(), response.getStatusCode()); assertEquals(PreferenceName.CONTINUE_ON_ERROR.getName(), response.getHeader(HttpHeader.PREFERENCE_APPLIED).iterator().next()); @@ -322,10 +396,10 @@ public class BatchClientITCase extends AbstractParamTecSvcITCase { assertNotNull(oDataResponse); assertEquals(HttpStatusCode.OK.getStatusCode(), oDataResponse.getStatusCode()); assertEquals(1, oDataResponse.getHeader(HttpHeader.ODATA_VERSION).size()); - assertEquals("4.0", oDataResponse.getHeader(HttpHeader.ODATA_VERSION).toArray()[0]); + assertEquals("4.0", oDataResponse.getHeader(HttpHeader.ODATA_VERSION).iterator().next()); assertEquals(1, oDataResponse.getHeader(HttpHeader.CONTENT_LENGTH).size()); - assertEquals("605", oDataResponse.getHeader(HttpHeader.CONTENT_LENGTH).toArray()[0]); - assertEquals(ContentType.JSON.toContentTypeString(), oDataResponse.getContentType()); + assertEquals(isJson() ? "605" : "2246", oDataResponse.getHeader(HttpHeader.CONTENT_LENGTH).iterator().next()); + assertContentType(oDataResponse.getContentType()); // Check second get request assertTrue(bodyIterator.hasNext()); @@ -347,17 +421,15 @@ public class BatchClientITCase extends AbstractParamTecSvcITCase { assertEquals(1, oDataResponse.getHeader(HttpHeader.ODATA_VERSION).size()); assertEquals("4.0", oDataResponse.getHeader(HttpHeader.ODATA_VERSION).iterator().next()); assertEquals(1, oDataResponse.getHeader(HttpHeader.CONTENT_LENGTH).size()); - assertEquals("517", oDataResponse.getHeader(HttpHeader.CONTENT_LENGTH).iterator().next()); - assertEquals(ContentType.JSON.toContentTypeString(), oDataResponse.getContentType()); + assertEquals(isJson() ? "517" : "2138", oDataResponse.getHeader(HttpHeader.CONTENT_LENGTH).iterator().next()); + assertContentType(oDataResponse.getContentType()); } @Test - public void changesetWithReferences() throws EdmPrimitiveTypeException, URISyntaxException { - // create your request - final ODataBatchRequest request = getClient().getBatchRequestFactory().getBatchRequest(SERVICE_URI); - final BatchManager streamManager = request.payloadManager(); + public void changesetWithReferences() { + BatchManager payload = getClient().getBatchRequestFactory().getBatchRequest(SERVICE_URI).payloadManager(); - final ODataChangeset changeset = streamManager.addChangeset(); + final ODataChangeset changeset = payload.addChangeset(); final ClientEntity entityESAllPrim = getFactory().newEntity(new FullQualifiedName(SERVICE_NAMESPACE, "ETAllPrim")); entityESAllPrim.getProperties().add(getFactory().newPrimitiveProperty("PropertyDouble", @@ -369,12 +441,12 @@ public class BatchClientITCase extends AbstractParamTecSvcITCase { .appendKeySegment(-365) .build())); - final URIBuilder uriBuilder = getClient().newURIBuilder(SERVICE_URI).appendEntitySetSegment("ESAllPrim"); + final URI uri = getClient().newURIBuilder(SERVICE_URI).appendEntitySetSegment("ESAllPrim").build(); // add create request final ODataEntityCreateRequest<ClientEntity> createReq = - getClient().getCUDRequestFactory().getEntityCreateRequest(uriBuilder.build(), entityESAllPrim); - createReq.setFormat(ContentType.JSON); + getClient().getCUDRequestFactory().getEntityCreateRequest(uri, entityESAllPrim); + createReq.setFormat(getContentType()); changeset.addRequest(createReq); // retrieve request reference @@ -387,11 +459,11 @@ public class BatchClientITCase extends AbstractParamTecSvcITCase { final ODataEntityUpdateRequest<ClientEntity> updateReq = getClient().getCUDRequestFactory().getEntityUpdateRequest( URI.create("$" + createRequestRef), UpdateType.PATCH, entityUpdate); - updateReq.setFormat(ContentType.JSON); + updateReq.setFormat(getContentType()); changeset.addRequest(updateReq); - final ODataBatchResponse response = streamManager.getResponse(); + final ODataBatchResponse response = payload.getResponse(); assertEquals(HttpStatusCode.ACCEPTED.getStatusCode(), response.getStatusCode()); final String cookie = response.getHeader(HttpHeader.SET_COOKIE).iterator().next(); @@ -399,25 +471,23 @@ public class BatchClientITCase extends AbstractParamTecSvcITCase { final Iterator<ODataBatchResponseItem> bodyIterator = response.getBody(); final ODataBatchResponseItem item = bodyIterator.next(); - assertTrue(item instanceof ODataChangesetResponseItem); - final ODataChangesetResponseItem chgitem = (ODataChangesetResponseItem) item; - assertTrue(chgitem.hasNext()); - ODataResponse res = chgitem.next(); + assertTrue(item.hasNext()); + ODataResponse res = item.next(); assertEquals(HttpStatusCode.CREATED.getStatusCode(), res.getStatusCode()); assertTrue(res instanceof ODataEntityCreateResponse); final ODataEntityCreateResponse<?> createResponse = ((ODataEntityCreateResponse<?>) res); - res = chgitem.next(); + res = item.next(); assertEquals(HttpStatusCode.OK.getStatusCode(), res.getStatusCode()); assertTrue(res instanceof ODataEntityUpdateResponse); final ODataEntitySetRequest<ClientEntitySet> req = getClient().getRetrieveRequestFactory().getEntitySetRequest( - new URI(createResponse.getHeader(HttpHeader.LOCATION).iterator().next() + "/NavPropertyETTwoPrimMany")); - req.setFormat(ContentType.JSON); + URI.create(createResponse.getHeader(HttpHeader.LOCATION).iterator().next() + "/NavPropertyETTwoPrimMany")); + req.setFormat(getContentType()); req.addCustomHeader(HttpHeader.COOKIE, cookie); final ODataRetrieveResponse<ClientEntitySet> getResponse = req.execute(); - assertEquals(32767, getResponse.getBody() + assertShortOrInt(32767, getResponse.getBody() .getEntities() .get(0) .getProperty("PropertyInt16") @@ -426,24 +496,22 @@ public class BatchClientITCase extends AbstractParamTecSvcITCase { } @Test - public void changesetBatchRequest() throws URISyntaxException { - final ODataBatchRequest request = getClient().getBatchRequestFactory().getBatchRequest(SERVICE_URI); - final BatchManager payload = request.payloadManager(); + public void changesetBatchRequest() { + BatchManager payload = getClient().getBatchRequestFactory().getBatchRequest(SERVICE_URI).payloadManager(); // ----------------------------- // - Append get request // ----------------------------- - appendGetRequest(payload, "ESAllPrim", 32767, false); // Without error + payload.addRequest(createGetRequest("ESAllPrim", 32767, false)); // ----------------------------- // - Append change set // ----------------------------- final ODataChangeset changeset = payload.addChangeset(); - // ------------------------ + // ----------------------------- // POST request (Insert) - URIBuilder targetURI = - getClient().newURIBuilder(SERVICE_URI).appendEntitySetSegment("ESAllPrim"); - URI editLink = targetURI.build(); + // ----------------------------- + URI targetURI = getClient().newURIBuilder(SERVICE_URI).appendEntitySetSegment("ESAllPrim").build(); ClientObjectFactory factory = getFactory(); ClientEntity postEntity = factory.newEntity(new FullQualifiedName(SERVICE_NAMESPACE, "ETAllPrim")); @@ -457,34 +525,32 @@ public class BatchClientITCase extends AbstractParamTecSvcITCase { factory.newPrimitiveValueBuilder().buildDouble(3.1415))); final ODataEntityCreateRequest<ClientEntity> createRequest = - getClient().getCUDRequestFactory().getEntityCreateRequest(editLink, postEntity); - createRequest.setFormat(ContentType.JSON); + getClient().getCUDRequestFactory().getEntityCreateRequest(targetURI, postEntity); + createRequest.setFormat(getContentType()); changeset.addRequest(createRequest); - // ------------------------ + // ----------------------------- // Patch request (Update) - targetURI = getClient().newURIBuilder(SERVICE_URI).appendEntitySetSegment("ESAllPrim").appendKeySegment(0); - editLink = targetURI.build(); - + // ----------------------------- ClientEntity patchEntity = factory.newEntity(new FullQualifiedName(SERVICE_NAMESPACE, "ETAllPrim")); - patchEntity.setEditLink(editLink); + patchEntity.setEditLink( + getClient().newURIBuilder(SERVICE_URI).appendEntitySetSegment("ESAllPrim").appendKeySegment(0).build()); patchEntity.getProperties().add(factory.newPrimitiveProperty("PropertyDouble", factory.newPrimitiveValueBuilder().buildDouble(3.1415))); ODataEntityUpdateRequest<ClientEntity> changeReq = getClient().getCUDRequestFactory().getEntityUpdateRequest(UpdateType.PATCH, patchEntity); - changeReq.setFormat(ContentType.JSON); + changeReq.setFormat(getContentType()); changeset.addRequest(changeReq); - // ------------------------ + // ----------------------------- // Patch request (Upsert) - targetURI = getClient().newURIBuilder(SERVICE_URI).appendEntitySetSegment("ESAllPrim").appendKeySegment(15); - editLink = targetURI.build(); - + // ----------------------------- patchEntity = factory.newEntity(new FullQualifiedName(SERVICE_NAMESPACE, "ETAllPrim")); - patchEntity.setEditLink(editLink); + patchEntity.setEditLink( + getClient().newURIBuilder(SERVICE_URI).appendEntitySetSegment("ESAllPrim").appendKeySegment(15).build()); patchEntity.getProperties().add(factory.newPrimitiveProperty("PropertyDouble", factory.newPrimitiveValueBuilder().buildDouble(3.1415))); @@ -496,13 +562,13 @@ public class BatchClientITCase extends AbstractParamTecSvcITCase { .build())); changeReq = getClient().getCUDRequestFactory().getEntityUpdateRequest(UpdateType.PATCH, patchEntity); - changeReq.setFormat(ContentType.JSON); + changeReq.setFormat(getContentType()); changeset.addRequest(changeReq); // ----------------------------- // - Append get request // ----------------------------- - appendGetRequest(payload, "ESAllPrim", 0, false); // Without error + payload.addRequest(createGetRequest("ESAllPrim", 0, false)); // ----------------------------- // - Fetch result @@ -520,7 +586,7 @@ public class BatchClientITCase extends AbstractParamTecSvcITCase { assertTrue(response0 instanceof ODataRetrieveResponse); @SuppressWarnings("unchecked") ODataRetrieveResponse<ClientEntity> retrieveResponse = (ODataRetrieveResponse<ClientEntity>) response0; - assertEquals(34, retrieveResponse.getBody() + assertShortOrInt(34, retrieveResponse.getBody() .getProperty("PropertyDecimal") .getPrimitiveValue() .toValue()); @@ -568,15 +634,15 @@ public class BatchClientITCase extends AbstractParamTecSvcITCase { .toValue()); } - private void appendGetRequest(final BatchManager manager, final String segment, final Object key, - final boolean isRelative) - throws URISyntaxException { - final URIBuilder targetURI = getClient().newURIBuilder(SERVICE_URI); - targetURI.appendEntitySetSegment(segment).appendKeySegment(key); - final URI uri = (isRelative) ? new URI(SERVICE_URI).relativize(targetURI.build()) : targetURI.build(); + private ODataEntityRequest<ClientEntity> createGetRequest(final String segment, final Object key, + final boolean isRelative) { + final URI targetURI = getClient().newURIBuilder(SERVICE_URI) + .appendEntitySetSegment(segment).appendKeySegment(key) + .build(); + final URI uri = isRelative ? URI.create(SERVICE_URI).relativize(targetURI) : targetURI; ODataEntityRequest<ClientEntity> queryReq = getClient().getRetrieveRequestFactory().getEntityRequest(uri); - queryReq.setFormat(ContentType.JSON); - manager.addRequest(queryReq); + queryReq.setAccept(getContentType().toContentTypeString()); + return queryReq; } } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/14278ac1/lib/server-api/src/main/java/org/apache/olingo/server/api/deserializer/batch/BatchDeserializerException.java ---------------------------------------------------------------------- diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/deserializer/batch/BatchDeserializerException.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/deserializer/batch/BatchDeserializerException.java index 6420ba6..24e2572 100644 --- a/lib/server-api/src/main/java/org/apache/olingo/server/api/deserializer/batch/BatchDeserializerException.java +++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/deserializer/batch/BatchDeserializerException.java @@ -39,6 +39,8 @@ public class BatchDeserializerException extends DeserializerException { /** parameter: line */ INVALID_CONTENT_ID, /** parameter: line */ + INVALID_HOST, + /** parameter: line */ INVALID_HTTP_VERSION, /** parameter: line */ INVALID_METHOD, @@ -55,8 +57,6 @@ public class BatchDeserializerException extends DeserializerException { /** parameter: line */ MISSING_CONTENT_ID, /** parameter: line */ - MISSING_CONTENT_TRANSFER_ENCODING, - /** parameter: line */ MISSING_CONTENT_TYPE, /** parameter: line */ MISSING_MANDATORY_HEADER, http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/14278ac1/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/batch/BatchLineReader.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/batch/BatchLineReader.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/batch/BatchLineReader.java index c209ade..15d7672 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/batch/BatchLineReader.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/batch/BatchLineReader.java @@ -33,8 +33,7 @@ public class BatchLineReader { private static final byte LF = '\n'; private static final int EOF = -1; private static final int BUFFER_SIZE = 8192; - private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); - private static final Charset CS_ISO_8859_1 = Charset.forName("iso-8859-1"); + private static final Charset DEFAULT_CHARSET = Charset.forName("ISO-8859-1"); public static final String BOUNDARY = "boundary"; public static final String DOUBLE_DASH = "--"; public static final String CRLF = "\r\n"; @@ -96,18 +95,17 @@ public class BatchLineReader { private void updateCurrentCharset(final String currentLine) { if (currentLine != null) { if (currentLine.startsWith(HttpHeader.CONTENT_TYPE)) { - //13 is content-type.length() + 1 for header value - String clValue = currentLine.substring(13, currentLine.length() - 2).trim(); - ContentType ct = ContentType.parse(clValue); - if (ct != null) { - String charsetString = ct.getParameter(ContentType.PARAMETER_CHARSET); - if (charsetString != null) { - currentCharset = Charset.forName(charsetString); - } else { - currentCharset = DEFAULT_CHARSET; - } - // boundary - String boundary = ct.getParameter(BOUNDARY); + final ContentType contentType = ContentType.parse( + currentLine.substring(HttpHeader.CONTENT_TYPE.length() + 1, currentLine.length() - 2).trim()); + if (contentType != null) { + final String charsetString = contentType.getParameter(ContentType.PARAMETER_CHARSET); + currentCharset = charsetString == null ? + contentType.isCompatible(ContentType.APPLICATION_JSON) || contentType.getSubtype().contains("xml") ? + Charset.forName("UTF-8") : + DEFAULT_CHARSET : + Charset.forName(charsetString); + + final String boundary = contentType.getParameter(BOUNDARY); if (boundary != null) { currentBoundary = DOUBLE_DASH + boundary; } @@ -121,12 +119,8 @@ public class BatchLineReader { } private boolean isBoundary(final String currentLine) { - if ((currentBoundary + CRLF).equals(currentLine)) { - return true; - } else if ((currentBoundary + DOUBLE_DASH + CRLF).equals(currentLine)) { - return true; - } - return false; + return (currentBoundary + CRLF).equals(currentLine) + || (currentBoundary + DOUBLE_DASH + CRLF).equals(currentLine); } String readLine() throws IOException { @@ -177,12 +171,8 @@ public class BatchLineReader { if (innerBuffer.position() == 0) { return null; } else { - String currentLine; - if (readState.isReadBody()) { - currentLine = new String(innerBuffer.array(), 0, innerBuffer.position(), getCurrentCharset()); - } else { - currentLine = new String(innerBuffer.array(), 0, innerBuffer.position(), CS_ISO_8859_1); - } + final String currentLine = new String(innerBuffer.array(), 0, innerBuffer.position(), + readState.isReadBody() ? currentCharset : DEFAULT_CHARSET); updateCurrentCharset(currentLine); return currentLine; } @@ -195,10 +185,6 @@ public class BatchLineReader { return limit; } - private Charset getCurrentCharset() { - return currentCharset; - } - /** * Read state indicator (whether currently the <code>body</code> or <code>header</code> part is read). */ http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/14278ac1/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/batch/BatchParserCommon.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/batch/BatchParserCommon.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/batch/BatchParserCommon.java index c9ae89e..d1f4392 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/batch/BatchParserCommon.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/batch/BatchParserCommon.java @@ -64,14 +64,14 @@ public class BatchParserCommon { public static String getBoundary(final String contentType, final int line) throws BatchDeserializerException { final ContentType type = parseContentType(contentType, ContentType.MULTIPART_MIXED, line); - String boundary = type.getParameter(BOUNDARY); + final String boundary = type.getParameter(BOUNDARY); if (boundary == null) { throw new BatchDeserializerException("Missing boundary.", BatchDeserializerException.MessageKeys.MISSING_BOUNDARY_DELIMITER, Integer.toString(line)); } - boundary = boundary.trim(); - if (PATTERN_BOUNDARY.matcher(boundary).matches()) { - return trimQuotes(boundary); + final Matcher matcher = PATTERN_BOUNDARY.matcher(boundary); + if (matcher.matches()) { + return matcher.group(1) == null ? matcher.group(2) : matcher.group(1); } else { throw new BatchDeserializerException("Invalid boundary format", BatchDeserializerException.MessageKeys.INVALID_BOUNDARY, Integer.toString(line)); @@ -111,13 +111,6 @@ public class BatchParserCommon { } } - private static String trimQuotes(final String boundary) { - if (boundary != null && boundary.length() >= 2 && boundary.startsWith("\"") && boundary.endsWith("\"")) { - return boundary.substring(1, boundary.length() - 1); - } - return boundary; - } - public static List<List<Line>> splitMessageByBoundary(final List<Line> message, final String boundary) throws BatchDeserializerException { final List<List<Line>> messageParts = new LinkedList<List<Line>>(); http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/14278ac1/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/batch/BatchRequestTransformator.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/batch/BatchRequestTransformator.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/batch/BatchRequestTransformator.java index b9174c9..006032b 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/batch/BatchRequestTransformator.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/batch/BatchRequestTransformator.java @@ -34,7 +34,6 @@ import org.apache.olingo.server.api.deserializer.batch.BatchDeserializerExceptio import org.apache.olingo.server.api.deserializer.batch.BatchRequestPart; public class BatchRequestTransformator { - private static final Charset DEFAULT_CHARSET = Charset.forName("utf-8"); private final String baseUri; private final String rawServiceResolutionUri; @@ -47,7 +46,7 @@ public class BatchRequestTransformator { final List<ODataRequest> requests = new LinkedList<ODataRequest>(); final List<BatchRequestPart> resultList = new ArrayList<BatchRequestPart>(); - validateBodyPartHeader(bodyPart); + validateHeaders(bodyPart.getHeaders(), bodyPart.isChangeSet()); for (BatchQueryOperation queryOperation : bodyPart.getRequests()) { requests.add(processQueryOperation(bodyPart, baseUri, queryOperation)); @@ -62,7 +61,7 @@ public class BatchRequestTransformator { if (bodyPart.isChangeSet()) { BatchQueryOperation encapsulatedQueryOperation = ((BatchChangeSetPart) queryOperation).getRequest(); handleContentId(queryOperation, encapsulatedQueryOperation); - validateHeader(queryOperation, true); + validateHeaders(queryOperation.getHeaders(), false); return createRequest(encapsulatedQueryOperation, baseUri, bodyPart.isChangeSet()); } else { @@ -99,20 +98,20 @@ public class BatchRequestTransformator { } private ODataRequest createRequest(final BatchQueryOperation operation, final String baseUri, - final boolean isChangeSet) - throws BatchDeserializerException { + final boolean isChangeSet) throws BatchDeserializerException { final HttpRequestStatusLine statusLine = new HttpRequestStatusLine(operation.getHttpStatusLine(), baseUri, rawServiceResolutionUri); statusLine.validateHttpMethod(isChangeSet); + BatchTransformatorCommon.validateHost(operation.getHeaders(), baseUri); validateBody(statusLine, operation); Charset charset = getCharset(operation); - InputStream bodyStrean = getBodyStream(operation, statusLine, charset); + InputStream bodyStream = getBodyStream(operation, statusLine, charset); validateForbiddenHeader(operation); final ODataRequest request = new ODataRequest(); - request.setBody(bodyStrean); + request.setBody(bodyStream); request.setMethod(statusLine.getMethod()); request.setRawBaseUri(statusLine.getRawBaseUri()); request.setRawODataPath(statusLine.getRawODataPath()); @@ -128,24 +127,25 @@ public class BatchRequestTransformator { } private Charset getCharset(final BatchQueryOperation operation) { - String ct = operation.getHeaders().getHeader(HttpHeader.CONTENT_TYPE); - if (ct != null) { - ContentType contentType = ContentType.parse(ct); - if (contentType != null) { - String charsetValue = contentType.getParameter(ContentType.PARAMETER_CHARSET); - if (charsetValue != null) { - return Charset.forName(charsetValue); + final ContentType contentType = ContentType.parse(operation.getHeaders().getHeader(HttpHeader.CONTENT_TYPE)); + if (contentType != null) { + final String charsetValue = contentType.getParameter(ContentType.PARAMETER_CHARSET); + if (charsetValue == null) { + if (contentType.isCompatible(ContentType.APPLICATION_JSON) || contentType.getSubtype().contains("xml")) { + return Charset.forName("UTF-8"); } + } else { + return Charset.forName(charsetValue); } } - return DEFAULT_CHARSET; + return Charset.forName("ISO-8859-1"); } private void validateForbiddenHeader(final BatchQueryOperation operation) throws BatchDeserializerException { final Header header = operation.getHeaders(); - if (header.exists(HttpHeader.AUTHORIZATION) || header.exists(HttpHeader.EXPECT) - || header.exists(HttpHeader.FROM) || header.exists(HttpHeader.MAX_FORWARDS) + if (header.exists(HttpHeader.WWW_AUTHENTICATE) || header.exists(HttpHeader.AUTHORIZATION) + || header.exists(HttpHeader.EXPECT) || header.exists(HttpHeader.FROM) || header.exists(HttpHeader.MAX_FORWARDS) || header.exists(HttpHeader.RANGE) || header.exists(HttpHeader.TE)) { throw new BatchDeserializerException("Forbidden header", MessageKeys.FORBIDDEN_HEADER, Integer.toString(header.getLineNumber())); @@ -153,8 +153,7 @@ public class BatchRequestTransformator { } private InputStream getBodyStream(final BatchQueryOperation operation, final HttpRequestStatusLine statusLine, - final Charset charset) - throws BatchDeserializerException { + final Charset charset) throws BatchDeserializerException { if (statusLine.getMethod().equals(HttpMethod.GET)) { return new ByteArrayInputStream(new byte[0]); } else { @@ -170,34 +169,23 @@ public class BatchRequestTransformator { private void validateBody(final HttpRequestStatusLine statusLine, final BatchQueryOperation operation) throws BatchDeserializerException { - if (statusLine.getMethod().equals(HttpMethod.GET) && isUnvalidGetRequestBody(operation)) { + if (statusLine.getMethod().equals(HttpMethod.GET) && isInvalidGetRequestBody(operation)) { throw new BatchDeserializerException("Invalid request line", MessageKeys.INVALID_CONTENT, Integer.toString(statusLine.getLineNumber())); } } - private boolean isUnvalidGetRequestBody(final BatchQueryOperation operation) { - return (operation.getBody().size() > 1) - || (operation.getBody().size() == 1 && !"".equals(operation.getBody().get(0).toString().trim())); + private boolean isInvalidGetRequestBody(final BatchQueryOperation operation) { + return operation.getBody().size() > 1 + || operation.getBody().size() == 1 && !operation.getBody().get(0).toString().trim().isEmpty(); } - private void validateHeader(final BatchPart bodyPart, final boolean isChangeSet) throws BatchDeserializerException { - final Header headers = bodyPart.getHeaders(); - - BatchTransformatorCommon.validateContentType(headers, ContentType.APPLICATION_HTTP); + private void validateHeaders(final Header headers, final boolean isChangeSet) throws BatchDeserializerException { if (isChangeSet) { - BatchTransformatorCommon.validateContentTransferEncoding(headers); - } - } - - private void validateBodyPartHeader(final BatchBodyPart bodyPart) throws BatchDeserializerException { - final Header header = bodyPart.getHeaders(); - - if (bodyPart.isChangeSet()) { - BatchTransformatorCommon.validateContentType(header, ContentType.MULTIPART_MIXED); + BatchTransformatorCommon.validateContentType(headers, ContentType.MULTIPART_MIXED); } else { - BatchTransformatorCommon.validateContentTransferEncoding(header); - BatchTransformatorCommon.validateContentType(header, ContentType.APPLICATION_HTTP); + BatchTransformatorCommon.validateContentTransferEncoding(headers); + BatchTransformatorCommon.validateContentType(headers, ContentType.APPLICATION_HTTP); } } } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/14278ac1/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/batch/BatchTransformatorCommon.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/batch/BatchTransformatorCommon.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/batch/BatchTransformatorCommon.java index d319f86..8b8e1d9 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/batch/BatchTransformatorCommon.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/batch/BatchTransformatorCommon.java @@ -18,6 +18,7 @@ */ package org.apache.olingo.server.core.deserializer.batch; +import java.net.URI; import java.util.List; import org.apache.olingo.commons.api.format.ContentType; @@ -47,23 +48,11 @@ public class BatchTransformatorCommon { if (contentTransferField != null) { final List<String> contentTransferValues = contentTransferField.getValues(); - if (contentTransferValues.size() == 1) { - String encoding = contentTransferValues.get(0); - - if (!BatchParserCommon.BINARY_ENCODING.equalsIgnoreCase(encoding)) { - throw new BatchDeserializerException("Invalid content transfer encoding", - MessageKeys.INVALID_CONTENT_TRANSFER_ENCODING, - Integer.toString(headers.getLineNumber())); - } - } else { + if (contentTransferValues.size() > 1 + || !BatchParserCommon.BINARY_ENCODING.equalsIgnoreCase(contentTransferValues.get(0))) { throw new BatchDeserializerException("Invalid Content-Transfer-Encoding header", - MessageKeys.INVALID_CONTENT_TRANSFER_ENCODING, - Integer.toString(headers.getLineNumber())); + MessageKeys.INVALID_CONTENT_TRANSFER_ENCODING, Integer.toString(headers.getLineNumber())); } - } else { - throw new BatchDeserializerException("Missing mandatory content transfer encoding", - MessageKeys.MISSING_CONTENT_TRANSFER_ENCODING, - Integer.toString(headers.getLineNumber())); } } @@ -71,10 +60,8 @@ public class BatchTransformatorCommon { final HeaderField contentLengthField = headers.getHeaderField(HttpHeader.CONTENT_LENGTH); if (contentLengthField != null && contentLengthField.getValues().size() == 1) { - final List<String> contentLengthValues = contentLengthField.getValues(); - try { - int contentLength = Integer.parseInt(contentLengthValues.get(0)); + final int contentLength = Integer.parseInt(contentLengthField.getValues().get(0)); if (contentLength < 0) { throw new BatchDeserializerException("Invalid content length", MessageKeys.INVALID_CONTENT_LENGTH, @@ -90,4 +77,16 @@ public class BatchTransformatorCommon { return -1; } + + public static void validateHost(final Header headers, final String baseUri) throws BatchDeserializerException { + final HeaderField hostField = headers.getHeaderField(HttpHeader.HOST); + + if (hostField != null) { + if (hostField.getValues().size() > 1 + || !URI.create(baseUri).getAuthority().equalsIgnoreCase(hostField.getValues().get(0).trim())) { + throw new BatchDeserializerException("Invalid Host header", + MessageKeys.INVALID_HOST, Integer.toString(headers.getLineNumber())); + } + } + } } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/14278ac1/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/batch/HttpRequestStatusLine.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/batch/HttpRequestStatusLine.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/batch/HttpRequestStatusLine.java index 38773de..f2d1178 100644 --- a/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/batch/HttpRequestStatusLine.java +++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/batch/HttpRequestStatusLine.java @@ -33,8 +33,8 @@ import org.apache.olingo.server.api.deserializer.batch.BatchDeserializerExceptio public class HttpRequestStatusLine { private static final Pattern PATTERN_RELATIVE_URI = Pattern.compile("([^/][^?]*)(?:\\?(.*))?"); - private static final Set<String> HTTP_CHANGE_SET_METHODS = new HashSet<String>(Arrays.asList(new String[] { "POST", - "PUT", "DELETE", "PATCH" })); + private static final Set<HttpMethod> HTTP_CHANGE_SET_METHODS = new HashSet<HttpMethod>(Arrays.asList( + new HttpMethod[] { HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE, HttpMethod.PATCH })); private static final String HTTP_VERSION = "HTTP/1.1"; final private Line statusLine; @@ -78,7 +78,12 @@ public class HttpRequestStatusLine { if (uri.isAbsolute()) { parseAbsoluteUri(rawUri, baseUri); } else { - parseRelativeUri(rawUri); + final URI base = URI.create(baseUri); + if (rawUri.startsWith(base.getRawPath())) { + parseRelativeUri(removeLeadingSlash(rawUri.substring(base.getRawPath().length()))); + } else { + parseRelativeUri(rawUri); + } } } catch (final URISyntaxException e) { throw new BatchDeserializerException("Malformed uri", e, MessageKeys.INVALID_URI, @@ -143,7 +148,7 @@ public class HttpRequestStatusLine { } public void validateHttpMethod(final boolean isChangeSet) throws BatchDeserializerException { - if (isChangeSet && !HTTP_CHANGE_SET_METHODS.contains(getMethod().toString())) { + if (isChangeSet && !HTTP_CHANGE_SET_METHODS.contains(getMethod())) { throw new BatchDeserializerException("Invalid change set method", MessageKeys.INVALID_CHANGESET_METHOD, Integer.toString(statusLine.getLineNumber())); } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/14278ac1/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 afe5535..831fe37 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 @@ -147,6 +147,7 @@ BatchDeserializerException.INVALID_CONTENT_TRANSFER_ENCODING=The Content-Transfe BatchDeserializerException.INVALID_CONTENT_TYPE=Invalid Content-Type HTTP header at line '%1$s'. BatchDeserializerException.UNEXPECTED_CONTENT_TYPE=Content-Type at line '%1$s' should be '%2$s' but is '%3$s'. BatchDeserializerException.INVALID_CONTENT_ID=Invalid Content-ID HTTP header at line '%1$s'. +BatchDeserializerException.INVALID_HOST=Invalid Host HTTP header at line '%1$s'. BatchDeserializerException.INVALID_HTTP_VERSION=The HTTP version must be HTTP/1.1 at line '%1$s'. BatchDeserializerException.INVALID_METHOD=Invalid HTTP method at line '%1$s'. BatchDeserializerException.INVALID_STATUS_LINE=Invalid HTTP status line at line '%1$s'. @@ -156,7 +157,6 @@ BatchDeserializerException.MISSING_BLANK_LINE=Missing blank line at line '%1$s'. BatchDeserializerException.MISSING_BOUNDARY_DELIMITER=Missing boundary delimiter at line '%1$s'. BatchDeserializerException.MISSING_CLOSE_DELIMITER=Missing close delimiter at line '%1$s'. BatchDeserializerException.MISSING_CONTENT_ID=Missing content-id at line '%1$s'. -BatchDeserializerException.MISSING_CONTENT_TRANSFER_ENCODING=Missing content transfer encoding at line '%1$s'. BatchDeserializerException.MISSING_CONTENT_TYPE=Missing content-type at line '%1$s'. BatchDeserializerException.MISSING_MANDATORY_HEADER=Missing mandatory header at line '%1$s'. BatchDeserializerException.INVALID_BASE_URI=The base URI does not match the service base URI at line '%1$s'. http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/14278ac1/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/batch/BatchLineReaderTest.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/batch/BatchLineReaderTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/batch/BatchLineReaderTest.java index ea202db..810c743 100644 --- a/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/batch/BatchLineReaderTest.java +++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/batch/BatchLineReaderTest.java @@ -18,12 +18,15 @@ */ package org.apache.olingo.server.core.deserializer.batch; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.Charset; import java.util.List; import org.junit.Test; @@ -45,7 +48,7 @@ public class BatchLineReaderTest { private static final String TEXT_EMPTY = ""; @Test - public void testSimpleText() throws Exception { + public void simpleText() throws Exception { final String TEXT = "Test"; BatchLineReader reader = create(TEXT); @@ -56,9 +59,8 @@ public class BatchLineReaderTest { } @Test - public void testNoText() throws Exception { - final String TEXT = ""; - BatchLineReader reader = create(TEXT); + public void noText() throws Exception { + BatchLineReader reader = create(TEXT_EMPTY); assertNull(reader.readLine()); assertNull(reader.readLine()); @@ -66,9 +68,8 @@ public class BatchLineReaderTest { } @Test - public void testNoBytes() throws Exception { - BatchLineReader reader = - new BatchLineReader(new ByteArrayInputStream(new byte[0])); + public void noBytes() throws Exception { + BatchLineReader reader = new BatchLineReader(new ByteArrayInputStream(new byte[0])); assertNull(reader.readLine()); assertNull(reader.readLine()); @@ -76,7 +77,7 @@ public class BatchLineReaderTest { } @Test - public void testCRLF() throws Exception { + public void CRLF() throws Exception { final String TEXT = "Test\r\n" + "Test2"; @@ -90,7 +91,7 @@ public class BatchLineReaderTest { } @Test - public void testLF() throws Exception { + public void LF() throws Exception { final String TEXT = "Test\n" + "Test2"; @@ -104,7 +105,7 @@ public class BatchLineReaderTest { } @Test - public void testCR() throws Exception { + public void CR() throws Exception { final String TEXT = "Test\r" + "Test2"; @@ -118,7 +119,7 @@ public class BatchLineReaderTest { } @Test - public void testCombined() throws Exception { + public void combined() throws Exception { BatchLineReader reader = create(TEXT_COMBINED); assertEquals("Test\r", reader.readLine()); @@ -138,7 +139,7 @@ public class BatchLineReaderTest { } @Test - public void testCombinedBufferSizeTwo() throws Exception { + public void combinedBufferSizeTwo() throws Exception { BatchLineReader reader = create(TEXT_COMBINED, 2); assertEquals("Test\r", reader.readLine()); @@ -158,20 +159,8 @@ public class BatchLineReaderTest { } @Test - public void testCombinedBufferSizeOne() throws Exception { - final String TEXT = "Test\r" + - "Test2\r\n" + - "Test3\n" + - "Test4\r" + - "\r" + - "\r\n" + - "\r\n" + - "Test5\n" + - "Test6\r\n" + - "Test7\n" + - "\r\n"; - - BatchLineReader reader = create(TEXT, 1); + public void combinedBufferSizeOne() throws Exception { + BatchLineReader reader = create(TEXT_COMBINED, 1); assertEquals("Test\r", reader.readLine()); assertEquals("Test2\r\n", reader.readLine()); @@ -183,7 +172,7 @@ public class BatchLineReaderTest { assertEquals("Test5\n", reader.readLine()); assertEquals("Test6\r\n", reader.readLine()); assertEquals("Test7\n", reader.readLine()); - assertEquals("\r\n", reader.readLine()); + assertEquals("\n", reader.readLine()); assertNull(reader.readLine()); assertNull(reader.readLine()); @@ -191,7 +180,7 @@ public class BatchLineReaderTest { } @Test - public void testDoubleLF() throws Exception { + public void doubleCR() throws Exception { final String TEXT = "Test\r" + "\r"; @@ -203,7 +192,7 @@ public class BatchLineReaderTest { } @Test - public void testLineEqualsAndHashCode() { + public void lineEqualsAndHashCode() { Line l1 = new Line("The first line", 1); Line l2 = new Line("The first line", 1); Line l3 = new Line("The second line", 2); @@ -214,19 +203,19 @@ public class BatchLineReaderTest { } @Test(expected = IllegalArgumentException.class) - public void testFailBufferSizeZero() throws Exception { + public void failBufferSizeZero() throws Exception { BatchLineReader reader = create(TEXT_EMPTY, 0); reader.close(); } @Test(expected = IllegalArgumentException.class) - public void testFailBufferSizeNegative() throws Exception { + public void failBufferSizeNegative() throws Exception { BatchLineReader reader = create(TEXT_EMPTY, -1); reader.close(); } @Test - public void testToList() throws Exception { + public void toList() throws Exception { BatchLineReader reader = create(TEXT_COMBINED); List<Line> stringList = reader.toLineList(); @@ -245,13 +234,48 @@ public class BatchLineReaderTest { reader.close(); } - private BatchLineReader create(final String inputString) throws Exception { - return new BatchLineReader(new ByteArrayInputStream(inputString - .getBytes("UTF-8"))); + @Test + public void specialCharacters() throws Exception { + final String text = "\r\n" + + "Content-Type: text/plain; charset=UTF-8\r\n" + + "\r\n" + + "äâ¬\r\n" + + "\uFDFC\r\n" // RIAL SIGN + // Unicode characters outside the Basic Multilingual Plane are stored + // in a Java String in two surrogate characters. + + String.valueOf(Character.toChars(0x1F603)); + BatchLineReader reader = create(text); + reader.readLine(); + reader.readLine(); + reader.readLine(); + assertEquals("äâ¬\r\n", reader.readLine()); + assertEquals("\uFDFC\r\n", reader.readLine()); + assertEquals(String.valueOf(Character.toChars(0x1F603)), reader.readLine()); + assertNull(reader.readLine()); + reader.close(); + } + + @Test + public void rawBytes() throws Exception { + byte[] content = new byte[Byte.MAX_VALUE - Byte.MIN_VALUE + 1]; + // binary content, not a valid UTF-8 representation of a string + for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) { + content[i - Byte.MIN_VALUE] = (byte) i; + } + BatchLineReader reader = new BatchLineReader(new ByteArrayInputStream(content)); + final String contentString = reader.readLine() // initial part up to '\n' + + reader.readLine() // second part from '\n' to '\r' + + reader.readLine(); // the rest + assertArrayEquals(content, contentString.getBytes(Charset.forName("ISO-8859-1"))); + assertNull(reader.readLine()); + reader.close(); + } + + private BatchLineReader create(final String inputString) throws IOException { + return new BatchLineReader(new ByteArrayInputStream(inputString.getBytes("UTF-8"))); } - private BatchLineReader create(final String inputString, final int bufferSize) throws Exception { - return new BatchLineReader(new ByteArrayInputStream(inputString - .getBytes("UTF-8")), bufferSize); + private BatchLineReader create(final String inputString, final int bufferSize) throws IOException { + return new BatchLineReader(new ByteArrayInputStream(inputString.getBytes("UTF-8")), bufferSize); } } http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/14278ac1/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/batch/BatchParserCommonTest.java ---------------------------------------------------------------------- diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/batch/BatchParserCommonTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/batch/BatchParserCommonTest.java index 6c27b19..95afe12 100644 --- a/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/batch/BatchParserCommonTest.java +++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/batch/BatchParserCommonTest.java @@ -27,11 +27,14 @@ import java.util.ArrayList; import java.util.List; import org.apache.olingo.commons.api.http.HttpHeader; +import org.apache.olingo.server.api.deserializer.batch.BatchDeserializerException; +import org.junit.Assert; import org.junit.Test; public class BatchParserCommonTest { private static final String CRLF = "\r\n"; + private static final String MULTIPART_MIXED = "multipart/mixed"; @Test public void multipleHeaders() throws Exception { @@ -161,6 +164,60 @@ public class BatchParserCommonTest { } @Test + public void boundaryParameter() throws Exception { + final String boundary = "boundary"; + final String contentType = MULTIPART_MIXED + "; boundary=" + boundary + " "; + Assert.assertEquals(boundary, BatchParserCommon.getBoundary(contentType, 0)); + } + + @Test + public void boundaryParameterWithQuotes() throws Exception { + final String boundary = "batch_1.2+34:2j)0?"; + final String contentType = MULTIPART_MIXED + "; boundary=\"" + boundary + "\""; + Assert.assertEquals(boundary, BatchParserCommon.getBoundary(contentType, 0)); + } + + @Test + public void boundaryParameterWithSpaces() throws Exception { + final String boundary = " boundary"; + final String contentType = MULTIPART_MIXED + "; boundary=\"" + boundary + "\" "; + Assert.assertEquals(boundary, BatchParserCommon.getBoundary(contentType, 0)); + } + + @Test + public void invalidContentType() throws Exception { + invalidBoundary("multipart;boundary=BOUNDARY", BatchDeserializerException.MessageKeys.INVALID_CONTENT_TYPE); + } + + @Test + public void contentTypeCharset() throws Exception { + final String contentType = MULTIPART_MIXED + "; charset=UTF-8;boundary=" + BatchParserCommon.BOUNDARY; + final String boundary = BatchParserCommon.getBoundary(contentType, 0); + Assert.assertEquals(BatchParserCommon.BOUNDARY, boundary); + } + + @Test + public void withoutBoundaryParameter() throws Exception { + invalidBoundary(MULTIPART_MIXED, BatchDeserializerException.MessageKeys.MISSING_BOUNDARY_DELIMITER); + } + + @Test + public void boundaryParameterWithoutQuote() throws Exception { + invalidBoundary(MULTIPART_MIXED + ";boundary=batch_1740-bb:84-2f7f", + BatchDeserializerException.MessageKeys.INVALID_BOUNDARY); + } + + @Test + public void boundaryEmpty() throws Exception { + invalidBoundary(MULTIPART_MIXED + ";boundary=\"\"", BatchDeserializerException.MessageKeys.INVALID_BOUNDARY); + } + + @Test + public void boundarySpace() throws Exception { + invalidBoundary(MULTIPART_MIXED + ";boundary=\" \"", BatchDeserializerException.MessageKeys.INVALID_BOUNDARY); + } + + @Test public void removeEndingCRLF() { String line = "Test" + CRLF; assertEquals("Test", BatchParserCommon.removeEndingCRLF(new Line(line, 1)).toString()); @@ -219,4 +276,13 @@ public class BatchParserCommonTest { return lineList; } + + private void invalidBoundary(final String contentType, final BatchDeserializerException.MessageKeys messageKey) { + try { + BatchParserCommon.getBoundary(contentType, 0); + Assert.fail("Expected exception not thrown."); + } catch (final BatchDeserializerException e) { + Assert.assertEquals(messageKey, e.getMessageKey()); + } + } }
