You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by ch...@apache.org on 2014/11/18 16:12:05 UTC
[04/22] olingo-odata4 git commit: [OLINGO-472] BatchParser first draft
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/32247295/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/writer/BatchResponseWriter.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/writer/BatchResponseWriter.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/writer/BatchResponseWriter.java
new file mode 100644
index 0000000..a063747
--- /dev/null
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/writer/BatchResponseWriter.java
@@ -0,0 +1,228 @@
+/*
+ * 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.batch.writer;
+
+import java.io.BufferedWriter;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStreamWriter;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.olingo.commons.api.http.HttpContentType;
+import org.apache.olingo.commons.api.http.HttpHeader;
+import org.apache.olingo.commons.api.http.HttpStatusCode;
+import org.apache.olingo.server.api.ODataResponse;
+import org.apache.olingo.server.api.batch.ODataResponsePart;
+import org.apache.olingo.server.core.batch.BatchException;
+import org.apache.olingo.server.core.batch.BatchException.MessageKeys;
+import org.apache.olingo.server.core.batch.parser.BatchParserCommon;
+import org.apache.olingo.server.core.serializer.utils.CircleStreamBuffer;
+
+public class BatchResponseWriter {
+ private static final int BUFFER_SIZE = 4096;
+ private static final String DOUBLE_DASH = "--";
+ private static final String COLON = ":";
+ private static final String SP = " ";
+ private static final String CRLF = "\r\n";
+
+ public void toODataResponse(final List<ODataResponsePart> batchResponse, final ODataResponse response)
+ throws IOException, BatchException {
+ final String boundary = generateBoundary("batch");
+
+ setStatusCode(response);
+ ResponseWriter writer = createBody(batchResponse, boundary);
+
+ response.setContent(writer.toInputStream());
+ setHttpHeader(response, writer, boundary);
+ }
+
+ private ResponseWriter createBody(final List<ODataResponsePart> batchResponses, final String boundary)
+ throws IOException, BatchException {
+ final ResponseWriter writer = new ResponseWriter();
+
+ for (final ODataResponsePart part : batchResponses) {
+ writer.append(getDashBoundary(boundary));
+
+ if (part.isChangeSet()) {
+ appendChangeSet(part, writer);
+ } else {
+ appendBodyPart(part.getResponses().get(0), writer, false);
+ }
+ }
+ writer.append(getCloseDelimiter(boundary));
+
+ return writer;
+ }
+
+ private void appendChangeSet(ODataResponsePart part, ResponseWriter writer) throws IOException, BatchException {
+ final String changeSetBoundary = generateBoundary("changeset");
+
+ appendChangeSetHeader(writer, changeSetBoundary);
+ writer.append(CRLF);
+
+ for (final ODataResponse response : part.getResponses()) {
+ writer.append(getDashBoundary(changeSetBoundary));
+ appendBodyPart(response, writer, true);
+ }
+
+ writer.append(getCloseDelimiter(changeSetBoundary));
+ writer.append(CRLF);
+ }
+
+ private void appendBodyPart(ODataResponse response, ResponseWriter writer, boolean isChangeSet) throws IOException,
+ BatchException {
+ byte[] body = getBody(response);
+
+ appendBodyPartHeader(response, writer, isChangeSet);
+ writer.append(CRLF);
+
+ appendStatusLine(response, writer);
+ appendResponseHeader(response, body.length, writer);
+ writer.append(CRLF);
+
+ writer.append(body);
+ writer.append(CRLF);
+ }
+
+ private byte[] getBody(final ODataResponse response) throws IOException {
+ final InputStream content = response.getContent();
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ if (content != null) {
+ byte[] buffer = new byte[BUFFER_SIZE];
+ int n;
+
+ while ((n = content.read(buffer, 0, buffer.length)) != -1) {
+ out.write(buffer, 0, n);
+ }
+ out.flush();
+
+ return out.toByteArray();
+ } else {
+ return new byte[0];
+ }
+ }
+
+ private void appendChangeSetHeader(ResponseWriter writer, final String changeSetBoundary) throws IOException {
+ appendHeader(HttpHeader.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED.toString() + "; boundary="
+ + changeSetBoundary, writer);
+ }
+
+ private void appendHeader(String name, String value, ResponseWriter writer) throws IOException {
+ writer.append(name)
+ .append(COLON)
+ .append(SP)
+ .append(value)
+ .append(CRLF);
+ }
+
+ private void appendStatusLine(ODataResponse response, ResponseWriter writer) throws IOException {
+ writer.append("HTTP/1.1")
+ .append(SP)
+ .append("" + response.getStatusCode())
+ .append(SP)
+ .append(HttpStatusCode.fromStatusCode(response.getStatusCode()).toString())
+ .append(CRLF);
+ }
+
+ private void appendResponseHeader(ODataResponse response, int contentLength, ResponseWriter writer)
+ throws IOException {
+ final Map<String, String> header = response.getHeaders();
+
+ for (final String key : header.keySet()) {
+ // Requests do never have content id header
+ if (!key.equalsIgnoreCase(BatchParserCommon.HTTP_CONTENT_ID)) {
+ appendHeader(key, header.get(key), writer);
+ }
+ }
+
+ appendHeader(HttpHeader.CONTENT_LENGTH, "" + contentLength, writer);
+ }
+
+ private void appendBodyPartHeader(ODataResponse response, ResponseWriter writer, boolean isChangeSet)
+ throws BatchException, IOException {
+ appendHeader(HttpHeader.CONTENT_TYPE, HttpContentType.APPLICATION_HTTP, writer);
+ appendHeader(BatchParserCommon.HTTP_CONTENT_TRANSFER_ENCODING, BatchParserCommon.BINARY_ENCODING, writer);
+
+ if (isChangeSet) {
+ if (response.getHeaders().get(BatchParserCommon.HTTP_CONTENT_ID) != null) {
+ appendHeader(BatchParserCommon.HTTP_CONTENT_ID, response.getHeaders().get(BatchParserCommon.HTTP_CONTENT_ID),
+ writer);
+ } else {
+ throw new BatchException("Missing content id", MessageKeys.MISSING_CONTENT_ID, "");
+ }
+ }
+ }
+
+ private void setHttpHeader(ODataResponse response, ResponseWriter writer, final String boundary) {
+ response.setHeader(HttpHeader.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED.toString() + "; boundary=" + boundary);
+ response.setHeader(HttpHeader.CONTENT_LENGTH, "" + writer.length());
+ }
+
+ private void setStatusCode(final ODataResponse response) {
+ response.setStatusCode(HttpStatusCode.ACCEPTED.getStatusCode());
+ }
+
+ private String getDashBoundary(String boundary) {
+ return DOUBLE_DASH + boundary + CRLF;
+ }
+
+ private String getCloseDelimiter(final String boundary) {
+ return DOUBLE_DASH + boundary + DOUBLE_DASH + CRLF;
+ }
+
+ private String generateBoundary(final String value) {
+ return value + "_" + UUID.randomUUID().toString();
+ }
+
+ private static class ResponseWriter {
+ private CircleStreamBuffer buffer = new CircleStreamBuffer();
+ private BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(buffer.getOutputStream()));
+ private int length = 0;
+
+ public ResponseWriter append(final String content) throws IOException {
+ length += content.length();
+ writer.write(content);
+
+ return this;
+ }
+
+ public ResponseWriter append(final byte[] content) throws IOException {
+ length += content.length;
+ writer.flush();
+ buffer.getOutputStream().write(content, 0, content.length);
+
+ return this;
+ }
+
+ public int length() {
+ return length;
+ }
+
+ public InputStream toInputStream() throws IOException {
+ writer.flush();
+ writer.close();
+
+ return buffer.getInputStream();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/32247295/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/writer/ODataResponsePartImpl.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/writer/ODataResponsePartImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/writer/ODataResponsePartImpl.java
new file mode 100644
index 0000000..a9711c9
--- /dev/null
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/writer/ODataResponsePartImpl.java
@@ -0,0 +1,44 @@
+/*
+ * 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.batch.writer;
+
+import java.util.List;
+
+import org.apache.olingo.server.api.ODataResponse;
+import org.apache.olingo.server.api.batch.ODataResponsePart;
+
+public class ODataResponsePartImpl implements ODataResponsePart {
+ private final List<ODataResponse> responses;
+ private final boolean isChangeSet;
+
+ public ODataResponsePartImpl(final List<ODataResponse> responses, final boolean isChangeSet) {
+ this.responses = responses;
+ this.isChangeSet = isChangeSet;
+ }
+
+ @Override
+ public List<ODataResponse> getResponses() {
+ return responses;
+ }
+
+ @Override
+ public boolean isChangeSet() {
+ return isChangeSet;
+ }
+}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/32247295/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 15f4ab8..7e58fc6 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
@@ -94,3 +94,24 @@ SerializerException.UNSUPPORTED_PROPERTY_TYPE=The type of the property '%1$s' is
SerializerException.INCONSISTENT_PROPERTY_TYPE=An inconsistency has been detected in the type definition of property '%1$s'.
SerializerException.MISSING_PROPERTY=The non-nullable property '%1$s' is missing.
SerializerException.WRONG_PROPERTY_VALUE=The value '%2$s' is not valid for property '%1$s'.
+
+BatchException.INVALID_BOUNDARY=Invalid boundary at line '%1$s'.
+BatchException.INVALID_CHANGESET_METHOD=Invalid method: a ChangeSet cannot contain retrieve requests at line '%1$s'.
+BatchException.INVALID_CONTENT=Retrieve requests must not contain any body content '%1$s'.
+BatchException.INVALID_CONTENT_LENGTH=Invalid content length: content length have to be an integer and positive at line '%1$s'.
+BatchException.INVALID_CONTENT_TRANSFER_ENCODING=The Content-Transfer-Encoding should be binary: line '%1$s'.
+BatchException.INVALID_CONTENT_TYPE=Content-Type should be '%1$s'.
+BatchException.INVALID_HEADER=Invalid header: '%1$s' at line '%2$s'.
+BatchException.INVALID_HTTP_VERSION=Invalid HTTP version: The version have to be HTTP/1.1 at line '%1$s'.
+BatchException.INVALID_METHOD=Invalid HTTP method at line '%1$s'.
+BatchException.INVALID_QUERY_OPERATION_METHOD=Invalid method: a query operation can only contain retrieve requests at line '%1$s'.
+BatchException.INVALID_STATUS_LINE=Invalid HTTP status line at line '%1$s'.
+BatchException.INVALID_URI=Invalid URI at line '%1$s'.
+BatchException.FORBIDDEN_HEADER=Forbidden header at line '%1$s'.
+BatchException.MISSING_BLANK_LINE=Missing blank line at line '%1$s'.
+BatchException.MISSING_BOUNDARY_DELIMITER=Missing boundary delimiter at line '%1$s'.
+BatchException.MISSING_CLOSE_DELIMITER=Missing close delimiter at line '%1$s'.
+BatchException.MISSING_CONTENT_ID=Missing content-id at line '%1$s'.
+BatchException.MISSING_CONTENT_TRANSFER_ENCODING=Missing content transfer encoding at line '%1$s'.
+BatchException.MISSING_CONTENT_TYPE=Missing content-type at line '%1$s'.
+BatchException.MISSING_MANDATORY_HEADER=Missing mandatory header at line '%1$s'.
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/32247295/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/BatchRequestParserTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/BatchRequestParserTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/BatchRequestParserTest.java
new file mode 100644
index 0000000..1d68307
--- /dev/null
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/BatchRequestParserTest.java
@@ -0,0 +1,1308 @@
+/*
+ * 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.batch;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URISyntaxException;
+import java.util.List;
+
+import org.apache.olingo.commons.api.http.HttpHeader;
+import org.apache.olingo.commons.api.http.HttpMethod;
+import org.apache.olingo.server.api.ODataRequest;
+import org.apache.olingo.server.api.batch.BatchRequestPart;
+import org.apache.olingo.server.core.batch.BatchException.MessageKeys;
+import org.apache.olingo.server.core.batch.parser.BatchParser;
+import org.apache.olingo.server.core.batch.parser.BatchParserCommon;
+import org.junit.Test;
+
+public class BatchRequestParserTest {
+
+ private static final String SERVICE_ROOT = "http://localhost/odata";
+ private static final String CONTENT_TYPE = "multipart/mixed;boundary=batch_8194-cf13-1f56";
+ private static final String CRLF = "\r\n";
+ private static final String MIME_HEADERS = "Content-Type: application/http" + CRLF
+ + "Content-Transfer-Encoding: binary" + CRLF;
+ private static final String GET_REQUEST = ""
+ + MIME_HEADERS
+ + CRLF
+ + "GET Employees('1')/EmployeeName HTTP/1.1" + CRLF
+ + CRLF
+ + CRLF;
+
+ @Test
+ public void test() throws IOException, BatchException, URISyntaxException {
+ final InputStream in = readFile("/batchWithPost.batch");
+ final List<BatchRequestPart> batchRequestParts = parse(in);
+
+ assertNotNull(batchRequestParts);
+ assertFalse(batchRequestParts.isEmpty());
+
+ for (BatchRequestPart object : batchRequestParts) {
+ if (!object.isChangeSet()) {
+ assertEquals(1, object.getRequests().size());
+ ODataRequest retrieveRequest = object.getRequests().get(0);
+ assertEquals(HttpMethod.GET, retrieveRequest.getMethod());
+
+ if (retrieveRequest.getHeaders(HttpHeader.ACCEPT_LANGUAGE) != null) {
+ assertEquals(3, retrieveRequest.getHeaders(HttpHeader.ACCEPT_LANGUAGE).size());
+ }
+
+ assertEquals(SERVICE_ROOT, retrieveRequest.getRawBaseUri());
+ assertEquals("/Employees('2')/EmployeeName", retrieveRequest.getRawODataPath());
+ assertEquals("http://localhost/odata/Employees('2')/EmployeeName?$format=json", retrieveRequest
+ .getRawRequestUri());
+ assertEquals("$format=json", retrieveRequest.getRawQueryPath());
+ } else {
+ List<ODataRequest> requests = object.getRequests();
+ for (ODataRequest request : requests) {
+
+ assertEquals(HttpMethod.PUT, request.getMethod());
+ assertEquals("100000", request.getHeader(HttpHeader.CONTENT_LENGTH));
+ assertEquals("application/json;odata=verbose", request.getHeader(HttpHeader.CONTENT_TYPE));
+
+ List<String> acceptHeader = request.getHeaders(HttpHeader.ACCEPT);
+ assertEquals(3, request.getHeaders(HttpHeader.ACCEPT).size());
+ assertEquals("application/atomsvc+xml;q=0.8", acceptHeader.get(0));
+ assertEquals("*/*;q=0.1", acceptHeader.get(2));
+
+ assertEquals("http://localhost/odata/Employees('2')/EmployeeName", request.getRawRequestUri());
+ assertEquals("http://localhost/odata", request.getRawBaseUri());
+ assertEquals("/Employees('2')/EmployeeName", request.getRawODataPath());
+ assertEquals("", request.getRawQueryPath()); // No query parameter
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testImageInContent() throws IOException, BatchException, URISyntaxException {
+ final InputStream contentInputStream = readFile("/batchWithContent.batch");
+ final String content = StringUtil.toString(contentInputStream);
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + MIME_HEADERS
+ + CRLF
+ + "GET Employees?$filter=Age%20gt%2040 HTTP/1.1" + CRLF
+ + "Accept: application/atomsvc+xml;q=0.8, application/json;odata=verbose;q=0.5, */*;q=0.1" + CRLF
+ + "MaxDataServiceVersion: 2.0" + CRLF
+ + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56" + CRLF
+ + "Content-Type: multipart/mixed; boundary=changeset_f980-1cb6-94dd" + CRLF
+ + CRLF
+ + "--changeset_f980-1cb6-94dd" + CRLF
+ + "content-type: Application/http" + CRLF
+ + "content-transfer-encoding: Binary" + CRLF
+ + "Content-ID: 1" + CRLF
+ + CRLF
+ + "POST Employees HTTP/1.1" + CRLF
+ + "Content-length: 100000" + CRLF
+ + "Content-type: application/octet-stream" + CRLF
+ + CRLF
+ + content
+ + CRLF
+ + "--changeset_f980-1cb6-94dd--" + CRLF
+ + "--batch_8194-cf13-1f56--";
+ final List<BatchRequestPart> BatchRequestParts = parse(batch);
+
+ for (BatchRequestPart part : BatchRequestParts) {
+ if (!part.isChangeSet()) {
+ assertEquals(1, part.getRequests().size());
+ final ODataRequest retrieveRequest = part.getRequests().get(0);
+
+ assertEquals(HttpMethod.GET, retrieveRequest.getMethod());
+ assertEquals("http://localhost/odata/Employees?$filter=Age%20gt%2040", retrieveRequest.getRawRequestUri());
+ assertEquals("http://localhost/odata", retrieveRequest.getRawBaseUri());
+ assertEquals("/Employees", retrieveRequest.getRawODataPath());
+ assertEquals("$filter=Age%20gt%2040", retrieveRequest.getRawQueryPath());
+ } else {
+ final List<ODataRequest> requests = part.getRequests();
+ for (ODataRequest request : requests) {
+ assertEquals(HttpMethod.POST, request.getMethod());
+ assertEquals("100000", request.getHeader(HttpHeader.CONTENT_LENGTH));
+ assertEquals("1", request.getHeader(BatchParserCommon.HTTP_CONTENT_ID));
+ assertEquals("application/octet-stream", request.getHeader(HttpHeader.CONTENT_TYPE));
+
+ final InputStream body = request.getBody();
+ assertEquals(content, StringUtil.toString(body));
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testPostWithoutBody() throws IOException, BatchException, URISyntaxException {
+ final String batch = CRLF
+ + "--batch_8194-cf13-1f56" + CRLF
+ + "Content-Type: multipart/mixed; boundary=changeset_f980-1cb6-94dd" + CRLF
+ + CRLF
+ + "--changeset_f980-1cb6-94dd" + CRLF
+ + MIME_HEADERS
+ + "Content-ID: changeRequest1" + CRLF
+ + CRLF
+ + "POST Employees('2') HTTP/1.1" + CRLF
+ + "Content-Length: 100" + CRLF
+ + "Content-Type: application/octet-stream" + CRLF
+ + CRLF
+ + CRLF
+ + "--changeset_f980-1cb6-94dd--" + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+ final List<BatchRequestPart> batchRequestParts = parse(batch);
+
+ for (BatchRequestPart object : batchRequestParts) {
+ if (object.isChangeSet()) {
+ final List<ODataRequest> requests = object.getRequests();
+
+ for (ODataRequest request : requests) {
+ assertEquals(HttpMethod.POST, request.getMethod());
+ assertEquals("100", request.getHeader(HttpHeader.CONTENT_LENGTH));
+ assertEquals("application/octet-stream", request.getHeader(HttpHeader.CONTENT_TYPE));
+ assertNotNull(request.getBody());
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testBoundaryParameterWithQuotas() throws BatchException, UnsupportedEncodingException {
+ final String contentType = "multipart/mixed; boundary=\"batch_1.2+34:2j)0?\"";
+ final String batch = ""
+ + "--batch_1.2+34:2j)0?" + CRLF
+ + GET_REQUEST
+ + "--batch_1.2+34:2j)0?--";
+ final BatchParser parser = new BatchParser(contentType, SERVICE_ROOT, "", true);
+ final List<BatchRequestPart> batchRequestParts = parser.parseBatchRequest(StringUtil.toInputStream(batch));
+
+ assertNotNull(batchRequestParts);
+ assertFalse(batchRequestParts.isEmpty());
+ }
+
+ @Test
+ public void testBatchWithInvalidContentType() throws UnsupportedEncodingException {
+ final String invalidContentType = "multipart;boundary=batch_1740-bb84-2f7f";
+ final String batch = ""
+ + "--batch_1740-bb84-2f7f" + CRLF
+ + GET_REQUEST
+ + "--batch_1740-bb84-2f7f--";
+ final BatchParser parser = new BatchParser(invalidContentType, SERVICE_ROOT, "", true);
+
+ try {
+ parser.parseBatchRequest(StringUtil.toInputStream(batch));
+ fail();
+ } catch (BatchException e) {
+ assertMessageKey(e, BatchException.MessageKeys.INVALID_CONTENT_TYPE);
+ }
+ }
+
+ @Test
+ public void testContentTypeCharset() throws BatchException {
+ final String contentType = "multipart/mixed; charset=UTF-8;boundary=batch_14d1-b293-b99a";
+ final String batch = ""
+ + "--batch_14d1-b293-b99a" + CRLF
+ + GET_REQUEST
+ + "--batch_14d1-b293-b99a--";
+ final BatchParser parser = new BatchParser(contentType, SERVICE_ROOT, "", true);
+ final List<BatchRequestPart> parts = parser.parseBatchRequest(StringUtil.toInputStream(batch));
+
+ assertEquals(1, parts.size());
+ }
+
+ @Test
+ public void testBatchWithoutBoundaryParameter() throws UnsupportedEncodingException {
+ final String invalidContentType = "multipart/mixed";
+ final String batch = ""
+ + "--batch_1740-bb84-2f7f" + CRLF
+ + GET_REQUEST
+ + "--batch_1740-bb84-2f7f--";
+ final BatchParser parser = new BatchParser(invalidContentType, SERVICE_ROOT, "", true);
+
+ try {
+ parser.parseBatchRequest(StringUtil.toInputStream(batch));
+ fail();
+ } catch (BatchException e) {
+ assertMessageKey(e, BatchException.MessageKeys.INVALID_CONTENT_TYPE);
+ }
+ }
+
+ @Test
+ public void testBoundaryParameterWithoutQuota() throws UnsupportedEncodingException {
+ final String invalidContentType = "multipart/mixed;boundary=batch_1740-bb:84-2f7f";
+ final String batch = ""
+ + "--batch_1740-bb:84-2f7f" + CRLF
+ + GET_REQUEST
+ + "--batch_1740-bb:84-2f7f--";
+ final BatchParser parser = new BatchParser(invalidContentType, SERVICE_ROOT, "", true);
+
+ try {
+ parser.parseBatchRequest(StringUtil.toInputStream(batch));
+ fail();
+ } catch (BatchException e) {
+ assertMessageKey(e, BatchException.MessageKeys.INVALID_BOUNDARY);
+ }
+ }
+
+ @Test
+ public void testWrongBoundaryString() throws BatchException, UnsupportedEncodingException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f5" + CRLF
+ + GET_REQUEST
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, BatchException.MessageKeys.MISSING_BOUNDARY_DELIMITER);
+ }
+
+ @Test
+ public void testMissingHttpVersion() throws UnsupportedEncodingException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + "Content-Type: application/http" + CRLF
+ + "Content-Transfer-Encoding:binary" + CRLF
+ + CRLF
+ + "GET Employees?$format=json" + CRLF
+ + "Host: localhost:8080" + CRLF
+ + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, BatchException.MessageKeys.INVALID_STATUS_LINE);
+ }
+
+ @Test
+ public void testMissingHttpVersion2() throws UnsupportedEncodingException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + "Content-Type: application/http" + CRLF
+ + "Content-Transfer-Encoding:binary" + CRLF
+ + CRLF
+ + "GET Employees?$format=json " + CRLF
+ + "Host: localhost:8080" + CRLF
+ + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, BatchException.MessageKeys.INVALID_HTTP_VERSION);
+ }
+
+ @Test
+ public void testMissingHttpVersion3() throws UnsupportedEncodingException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + "Content-Type: application/http" + CRLF
+ + "Content-Transfer-Encoding:binary" + CRLF
+ + CRLF
+ + "GET Employees?$format=json SMTP:3.1" + CRLF
+ + "Host: localhost:8080" + CRLF
+ + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, BatchException.MessageKeys.INVALID_HTTP_VERSION);
+ }
+
+ @Test
+ public void testBoundaryWithoutHyphen() throws UnsupportedEncodingException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + GET_REQUEST
+ + "batch_8194-cf13-1f56" + CRLF
+ + GET_REQUEST
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, BatchException.MessageKeys.INVALID_CONTENT);
+ }
+
+ @Test
+ public void testNoBoundaryString() throws UnsupportedEncodingException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + GET_REQUEST
+ // + no boundary string
+ + GET_REQUEST
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, BatchException.MessageKeys.INVALID_CONTENT);
+ }
+
+ @Test
+ public void testBatchBoundaryEqualsChangeSetBoundary() throws UnsupportedEncodingException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + "Content-Type: multipart/mixed;boundary=batch_8194-cf13-1f56" + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56" + CRLF
+ + MIME_HEADERS
+ + CRLF
+ + "PUT Employees('2')/EmployeeName HTTP/1.1" + CRLF
+ + "Accept: application/atomsvc+xml;q=0.8, application/json;odata=verbose;q=0.5, */*;q=0.1" + CRLF
+ + "Content-Type: application/json;odata=verbose" + CRLF
+ + "MaxDataServiceVersion: 2.0" + CRLF
+ + CRLF
+ + "{\"EmployeeName\":\"Frederic Fall MODIFIED\"}" + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--"
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, BatchException.MessageKeys.MISSING_BLANK_LINE);
+ }
+
+ @Test
+ public void testNoContentType() throws UnsupportedEncodingException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + "Content-Transfer-Encoding: binary" + CRLF
+ + CRLF
+ + "GET Employees('1')/EmployeeName HTTP/1.1" + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, BatchException.MessageKeys.MISSING_CONTENT_TYPE);
+ }
+
+ @Test
+ public void testMimeHeaderContentType() throws UnsupportedEncodingException {
+ final String batch = "--batch_8194-cf13-1f56" + CRLF
+ + "Content-Type: text/plain" + CRLF
+ + "Content-Transfer-Encoding: binary" + CRLF
+ + CRLF
+ + "GET Employees('1')/EmployeeName HTTP/1.1" + CRLF
+ + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, BatchException.MessageKeys.INVALID_CONTENT_TYPE);
+ }
+
+ @Test
+ public void testMimeHeaderEncoding() throws UnsupportedEncodingException {
+ String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + "Content-Type: application/http" + CRLF
+ + "Content-Transfer-Encoding: 8bit" + CRLF
+ + CRLF
+ + "GET Employees('1')/EmployeeName HTTP/1.1" + CRLF
+ + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, BatchException.MessageKeys.INVALID_CONTENT_TRANSFER_ENCODING);
+ }
+
+ @Test
+ public void testGetRequestMissingCRLF() throws UnsupportedEncodingException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + MIME_HEADERS
+ + "Content-ID: 1" + CRLF
+ + CRLF
+ + "GET Employees('1')/EmployeeName HTTP/1.1" + CRLF
+ // + CRLF // Belongs to the GET request
+ + CRLF // Belongs to the
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, BatchException.MessageKeys.MISSING_BLANK_LINE);
+ }
+
+ @Test
+ public void testInvalidMethodForBatch() throws UnsupportedEncodingException {
+ final String batch = "--batch_8194-cf13-1f56" + CRLF
+ + MIME_HEADERS
+ + CRLF
+ + "POST Employees('1')/EmployeeName HTTP/1.1" + CRLF
+ + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, BatchException.MessageKeys.INVALID_QUERY_OPERATION_METHOD);
+ }
+
+ @Test
+ public void testNoBoundaryFound() throws UnsupportedEncodingException {
+ final String batch = "batch_8194-cf13-1f56" + CRLF
+ + MIME_HEADERS
+ + CRLF
+ + "POST Employees('1')/EmployeeName HTTP/1.1" + CRLF
+ + CRLF;
+
+ parseInvalidBatchBody(batch, BatchException.MessageKeys.MISSING_BOUNDARY_DELIMITER);
+ }
+
+ @Test
+ public void testBadRequest() throws UnsupportedEncodingException {
+ final String batch = "This is a bad request. There is no syntax and also no semantic";
+
+ parseInvalidBatchBody(batch, BatchException.MessageKeys.MISSING_BOUNDARY_DELIMITER);
+ }
+
+ @Test
+ public void testNoMethod() throws UnsupportedEncodingException {
+ final String batch = "--batch_8194-cf13-1f56" + CRLF
+ + MIME_HEADERS
+ + CRLF
+ + /* GET */"Employees('1')/EmployeeName HTTP/1.1" + CRLF
+ + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, BatchException.MessageKeys.INVALID_STATUS_LINE);
+ }
+
+ @Test
+ public void testInvalidMethodForChangeset() throws UnsupportedEncodingException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + "Content-Type: multipart/mixed; boundary=changeset_f980-1cb6-94dd" + CRLF
+ + CRLF
+ + "--changeset_f980-1cb6-94dd" + CRLF
+ + MIME_HEADERS
+ + "Content-Id: 1" + CRLF
+ + CRLF
+ + "GET Employees('2')/EmployeeName HTTP/1.1" + CRLF
+ + "Content-Type: application/json;odata=verbose" + CRLF
+ + "MaxDataServiceVersion: 2.0" + CRLF
+ + CRLF
+ + CRLF
+ + "--changeset_f980-1cb6-94dd--"
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, BatchException.MessageKeys.INVALID_CHANGESET_METHOD);
+ }
+
+ @Test
+ public void testInvalidChangeSetBoundary() throws UnsupportedEncodingException {
+ final String batch = "--batch_8194-cf13-1f56" + CRLF
+ + "Content-Type: multipart/mixed;boundary=changeset_f980-1cb6-94dd" + CRLF
+ + CRLF
+ + "--changeset_f980-1cb6-94d"/* +"d" */+ CRLF
+ + MIME_HEADERS
+ + CRLF
+ + "POST Employees('2') HTTP/1.1" + CRLF
+ + "Content-Type: application/json;odata=verbose" + CRLF
+ + "MaxDataServiceVersion: 2.0" + CRLF
+ + CRLF
+ + "--changeset_f980-1cb6-94dd--" + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, BatchException.MessageKeys.MISSING_BOUNDARY_DELIMITER);
+ }
+
+ @Test
+ public void testNestedChangeset() throws UnsupportedEncodingException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + "Content-Type: multipart/mixed;boundary=changeset_f980-1cb6-94dd" + CRLF
+ + CRLF
+ + "--changeset_f980-1cb6-94dd" + CRLF
+ + "Content-Transfer-Encoding: binary" + CRLF
+ + "Content-Type: multipart/mixed;boundary=changeset_f980-1cb6-94dd2" + CRLF
+ + CRLF
+ + "--changeset_f980-1cb6-94dd2" + CRLF
+ + MIME_HEADERS
+ + "Content-Id: 1" + CRLF
+ + CRLF
+ + "POST Employees('2') HTTP/1.1" + CRLF
+ + "Content-Type: application/json;odata=verbose" + CRLF
+ + "MaxDataServiceVersion: 2.0" + CRLF
+ + "Content-Id: 2"
+ + CRLF
+ + "--changeset_f980-1cb6-94dd--" + CRLF
+ + CRLF
+ + "--changeset_f980-1cb6-94dd--" + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, BatchException.MessageKeys.INVALID_CONTENT_TYPE);
+ }
+
+ @Test
+ public void testMissingContentTransferEncoding() throws UnsupportedEncodingException {
+ final String batch = "--batch_8194-cf13-1f56" + CRLF
+ + "Content-Type: multipart/mixed;boundary=changeset_f980-1cb6-94dd" + CRLF
+ + CRLF
+ + "--changeset_f980-1cb6-94dd" + CRLF
+ + "Content-Id: 1" + CRLF
+ + "Content-Type: application/http" + CRLF
+ // + "Content-Transfer-Encoding: binary" + CRLF
+ + CRLF
+ + "POST Employees('2') HTTP/1.1" + CRLF
+ + "Content-Type: application/json;odata=verbose" + CRLF
+ + "MaxDataServiceVersion: 2.0" + CRLF
+ + CRLF
+ + CRLF
+ + "--changeset_f980-1cb6-94dd--" + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, BatchException.MessageKeys.MISSING_CONTENT_TRANSFER_ENCODING);
+ }
+
+ @Test
+ public void testMissingContentType() throws UnsupportedEncodingException {
+ final String batch = "--batch_8194-cf13-1f56" + CRLF
+ + "Content-Type: multipart/mixed;boundary=changeset_f980-1cb6-94dd" + CRLF
+ + CRLF
+ + "--changeset_f980-1cb6-94dd" + CRLF
+ + "Content-Id: 1"
+ // + "Content-Type: application/http" + CRLF
+ + "Content-Transfer-Encoding: binary" + CRLF
+ + CRLF
+ + "POST Employees('2') HTTP/1.1" + CRLF
+ + "Content-Type: application/json;odata=verbose" + CRLF
+ + "MaxDataServiceVersion: 2.0" + CRLF
+ + CRLF
+ + CRLF
+ + "--changeset_f980-1cb6-94dd--" + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, BatchException.MessageKeys.MISSING_CONTENT_TYPE);
+ }
+
+ @Test
+ public void testNoCloseDelimiter() throws BatchException, UnsupportedEncodingException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + GET_REQUEST;
+
+ parseInvalidBatchBody(batch, BatchException.MessageKeys.MISSING_CLOSE_DELIMITER);
+ }
+
+ @Test
+ public void testNoCloseDelimiter2() throws BatchException, UnsupportedEncodingException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + MIME_HEADERS
+ + CRLF
+ + "GET Employees('1')/EmployeeName HTTP/1.1" + CRLF;
+
+ parseInvalidBatchBody(batch, BatchException.MessageKeys.MISSING_CLOSE_DELIMITER);
+ }
+
+ @Test
+ public void testInvalidUri() throws UnsupportedEncodingException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + MIME_HEADERS
+ + CRLF
+ + "GET http://localhost/aa/odata/Employees('1')/EmployeeName HTTP/1.1" + CRLF
+ + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, BatchException.MessageKeys.INVALID_URI);
+ }
+
+ @Test
+ public void testUriWithAbsolutePath() throws BatchException, UnsupportedEncodingException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + MIME_HEADERS
+ + CRLF
+ + "GET /odata/Employees('1')/EmployeeName HTTP/1.1" + CRLF
+ + "Host: http://localhost" + CRLF
+ + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ final List<BatchRequestPart> parts = parse(batch);
+ assertEquals(1, parts.size());
+
+ final BatchRequestPart part = parts.get(0);
+ assertEquals(1, part.getRequests().size());
+ final ODataRequest request = part.getRequests().get(0);
+
+ assertEquals("http://localhost/odata/Employees('1')/EmployeeName", request.getRawRequestUri());
+ assertEquals("http://localhost/odata", request.getRawBaseUri());
+ assertEquals("/Employees('1')/EmployeeName", request.getRawODataPath());
+ assertEquals("", request.getRawQueryPath());
+ }
+
+ @Test
+ public void testUriWithAbsolutePathMissingHostHeader() throws BatchException, UnsupportedEncodingException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + MIME_HEADERS
+ + CRLF
+ + "GET /odata/Employees('1')/EmployeeName HTTP/1.1" + CRLF
+ + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, MessageKeys.MISSING_MANDATORY_HEADER);
+ }
+
+ @Test
+ public void testUriWithAbsolutePathMissingHostDulpicatedHeader() throws BatchException, UnsupportedEncodingException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + MIME_HEADERS
+ + CRLF
+ + "GET /odata/Employees('1')/EmployeeName HTTP/1.1" + CRLF
+ + "Host: http://localhost" + CRLF
+ + "Host: http://localhost/odata" + CRLF
+ + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, MessageKeys.MISSING_MANDATORY_HEADER);
+ }
+
+ @Test
+ public void testUriWithAbsolutePathOtherHost() throws BatchException, UnsupportedEncodingException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + MIME_HEADERS
+ + CRLF
+ + "GET /odata/Employees('1')/EmployeeName HTTP/1.1" + CRLF
+ + "Host: http://localhost2" + CRLF
+ + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, MessageKeys.INVALID_URI);
+ }
+
+ @Test
+ public void testUriWithAbsolutePathWrongPath() throws BatchException, UnsupportedEncodingException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + MIME_HEADERS
+ + CRLF
+ + "GET /myservice/Employees('1')/EmployeeName HTTP/1.1" + CRLF
+ + "Host: http://localhost" + CRLF
+ + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, MessageKeys.INVALID_URI);
+ }
+
+ @Test
+ public void testNoCloseDelimiter3() throws UnsupportedEncodingException {
+ final String batch = "--batch_8194-cf13-1f56" + CRLF + GET_REQUEST + "--batch_8194-cf13-1f56-"/* no hyphen */;
+
+ parseInvalidBatchBody(batch, BatchException.MessageKeys.MISSING_CLOSE_DELIMITER);
+ }
+
+ @Test
+ public void testNegativeContentLengthChangeSet() throws BatchException, IOException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + "Content-Type: multipart/mixed; boundary=changeset_f980-1cb6-94dd" + CRLF
+ + CRLF
+ + "--changeset_f980-1cb6-94dd" + CRLF
+ + MIME_HEADERS
+ + "Content-ID: 1" + CRLF
+ + "Content-Length: -2" + CRLF
+ + CRLF
+ + "PUT EmployeeName HTTP/1.1" + CRLF
+ + "Content-Type: application/json;odata=verbose" + CRLF
+ + "Content-Id: 1" + CRLF
+ + CRLF
+ + "{\"EmployeeName\":\"Peter Fall\"}" + CRLF
+ + "--changeset_f980-1cb6-94dd--" + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parse(batch);
+ }
+
+ @Test
+ public void testNegativeContentLengthRequest() throws BatchException, IOException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + "Content-Type: multipart/mixed; boundary=changeset_f980-1cb6-94dd" + CRLF
+ + CRLF
+ + "--changeset_f980-1cb6-94dd" + CRLF
+ + MIME_HEADERS
+ + "Content-ID: 1" + CRLF
+ + CRLF
+ + "PUT EmployeeName HTTP/1.1" + CRLF
+ + "Content-Type: application/json;odata=verbose" + CRLF
+ + "Content-Id: 1" + CRLF
+ + "Content-Length: 2" + CRLF
+ + CRLF
+ + "{\"EmployeeName\":\"Peter Fall\"}" + CRLF
+ + "--changeset_f980-1cb6-94dd--" + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parse(batch);
+ }
+
+ @Test
+ public void testContentLengthGreatherThanBodyLength() throws BatchException, IOException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + "Content-Type: multipart/mixed; boundary=changeset_f980-1cb6-94dd" + CRLF
+ + CRLF
+ + "--changeset_f980-1cb6-94dd" + CRLF
+ + MIME_HEADERS
+ + "Content-ID: 1" + CRLF
+ + CRLF
+ + "PUT Employee/Name HTTP/1.1" + CRLF
+ + "Content-Type: application/json;odata=verbose" + CRLF
+ + "Content-Length: 100000" + CRLF
+ + CRLF
+ + "{\"EmployeeName\":\"Peter Fall\"}" + CRLF
+ + "--changeset_f980-1cb6-94dd--" + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+ final List<BatchRequestPart> batchRequestParts = parse(batch);
+
+ assertNotNull(batchRequestParts);
+
+ for (BatchRequestPart multipart : batchRequestParts) {
+ if (multipart.isChangeSet()) {
+ assertEquals(1, multipart.getRequests().size());
+
+ final ODataRequest request = multipart.getRequests().get(0);
+ assertEquals("{\"EmployeeName\":\"Peter Fall\"}", StringUtil.toString(request.getBody()));
+ }
+ }
+ }
+
+ @Test
+ public void testContentLengthSmallerThanBodyLength() throws BatchException, IOException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + "Content-Type: multipart/mixed; boundary=changeset_f980-1cb6-94dd" + CRLF
+ + CRLF
+ + "--changeset_f980-1cb6-94dd" + CRLF
+ + MIME_HEADERS
+ + "Content-ID: 1" + CRLF
+ + CRLF
+ + "PUT EmployeeName HTTP/1.1" + CRLF
+ + "Content-Type: application/json;odata=verbose" + CRLF
+ + "Content-Length: 10" + CRLF
+ + CRLF
+ + "{\"EmployeeName\":\"Peter Fall\"}" + CRLF
+ + "--changeset_f980-1cb6-94dd--" + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+ final List<BatchRequestPart> batchRequestParts = parse(batch);
+
+ assertNotNull(batchRequestParts);
+
+ for (BatchRequestPart multipart : batchRequestParts) {
+ if (multipart.isChangeSet()) {
+ assertEquals(1, multipart.getRequests().size());
+
+ final ODataRequest request = multipart.getRequests().get(0);
+ assertEquals("{\"Employee", StringUtil.toString(request.getBody()));
+ }
+ }
+ }
+
+ @Test
+ public void testNonNumericContentLength() throws UnsupportedEncodingException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + "Content-Type: multipart/mixed; boundary=changeset_f980-1cb6-94dd" + CRLF
+ + CRLF
+ + "--changeset_f980-1cb6-94dd" + CRLF
+ + MIME_HEADERS
+ + "Content-ID: 1" + CRLF
+ + CRLF
+ + "PUT EmployeeName HTTP/1.1" + CRLF
+ + "Content-Type: application/json;odata=verbose" + CRLF
+ + "Content-Length: 10abc" + CRLF
+ + CRLF
+ + "{\"EmployeeName\":\"Peter Fall\"}" + CRLF
+ + "--changeset_f980-1cb6-94dd--" + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, BatchException.MessageKeys.INVALID_HEADER);
+ }
+
+ @Test
+ public void testNonStrictParser() throws BatchException, IOException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + "Content-Type: multipart/mixed;boundary=changeset_8194-cf13-1f56" + CRLF
+ + "--changeset_8194-cf13-1f56" + CRLF
+ + MIME_HEADERS
+ + "Content-ID: myRequest" + CRLF
+ + "PUT Employees('2')/EmployeeName HTTP/1.1" + CRLF
+ + "Accept: application/atomsvc+xml;q=0.8, application/json;odata=verbose;q=0.5, */*;q=0.1" + CRLF
+ + "Content-Type: application/json;odata=verbose" + CRLF
+ + "MaxDataServiceVersion: 2.0" + CRLF
+ + "{\"EmployeeName\":\"Frederic Fall MODIFIED\"}" + CRLF
+ + "--changeset_8194-cf13-1f56--" + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ final List<BatchRequestPart> requests = parse(batch, false);
+
+ assertNotNull(requests);
+ assertEquals(1, requests.size());
+
+ final BatchRequestPart part = requests.get(0);
+ assertTrue(part.isChangeSet());
+ assertNotNull(part.getRequests());
+ assertEquals(1, part.getRequests().size());
+
+ final ODataRequest changeRequest = part.getRequests().get(0);
+ assertEquals("{\"EmployeeName\":\"Frederic Fall MODIFIED\"}",
+ StringUtil.toString(changeRequest.getBody()));
+ assertEquals("application/json;odata=verbose", changeRequest.getHeader(HttpHeader.CONTENT_TYPE));
+ assertEquals(HttpMethod.PUT, changeRequest.getMethod());
+ }
+
+ @Test
+ public void testNonStrictParserMoreCRLF() throws UnsupportedEncodingException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + "Content-Type: multipart/mixed;boundary=changeset_8194-cf13-1f56" + CRLF
+ + "--changeset_8194-cf13-1f56" + CRLF
+ + MIME_HEADERS
+ + CRLF
+ + CRLF // Only one CRLF allowed
+ + "PUT Employees('2')/EmployeeName HTTP/1.1" + CRLF
+ + "Accept: application/atomsvc+xml;q=0.8, application/json;odata=verbose;q=0.5, */*;q=0.1" + CRLF
+ + "Content-Type: application/json;odata=verbose" + CRLF
+ + "MaxDataServiceVersion: 2.0" + CRLF
+ + "{\"EmployeeName\":\"Frederic Fall MODIFIED\"}" + CRLF
+ + "--changeset_8194-cf13-1f56--" + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, BatchException.MessageKeys.INVALID_STATUS_LINE, false);
+ }
+
+ @Test
+ public void testContentId() throws BatchException, UnsupportedEncodingException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + MIME_HEADERS
+ + CRLF
+ + "GET Employees HTTP/1.1" + CRLF
+ + "accept: */*,application/atom+xml,application/atomsvc+xml,application/xml" + CRLF
+ + "Content-Id: BBB" + CRLF
+ + CRLF + CRLF
+ + "--batch_8194-cf13-1f56" + CRLF
+ + "Content-Type: multipart/mixed; boundary=changeset_f980-1cb6-94dd" + CRLF
+ + CRLF
+ + "--changeset_f980-1cb6-94dd" + CRLF
+ + MIME_HEADERS
+ + "Content-Id: 1" + CRLF
+ + CRLF
+ + "POST Employees HTTP/1.1" + CRLF
+ + "Content-type: application/octet-stream" + CRLF
+ + CRLF
+ + "/9j/4AAQSkZJRgABAQEBLAEsAAD/4RM0RXhpZgAATU0AKgAAAAgABwESAAMAAAABAAEAAAEaAAUAAAABAAAAYgEbAAUAAAA" + CRLF
+ + CRLF
+ + "--changeset_f980-1cb6-94dd" + CRLF
+ + MIME_HEADERS
+ + CRLF
+ + "PUT $1/EmployeeName HTTP/1.1" + CRLF
+ + "Content-Type: application/json;odata=verbose" + CRLF
+ + "Content-Id: 2" + CRLF
+ + CRLF
+ + "{\"EmployeeName\":\"Peter Fall\"}" + CRLF
+ + "--changeset_f980-1cb6-94dd--" + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ final List<BatchRequestPart> batchRequestParts = parse(batch);
+ assertNotNull(batchRequestParts);
+
+ for (BatchRequestPart multipart : batchRequestParts) {
+ if (!multipart.isChangeSet()) {
+ assertEquals(1, multipart.getRequests().size());
+ final ODataRequest retrieveRequest = multipart.getRequests().get(0);
+
+ assertEquals("BBB", retrieveRequest.getHeader(BatchParserCommon.HTTP_CONTENT_ID));
+ } else {
+ for (ODataRequest request : multipart.getRequests()) {
+ if (HttpMethod.POST.equals(request.getMethod())) {
+ assertEquals("1", request.getHeader(BatchParserCommon.HTTP_CONTENT_ID));
+ } else if (HttpMethod.PUT.equals(request.getMethod())) {
+ assertEquals("2", request.getHeader(BatchParserCommon.HTTP_CONTENT_ID));
+ assertEquals("/$1/EmployeeName", request.getRawODataPath());
+ assertEquals("http://localhost/odata/$1/EmployeeName", request.getRawRequestUri());
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testNoContentId() throws BatchException, UnsupportedEncodingException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + MIME_HEADERS
+ + CRLF
+ + "GET Employees HTTP/1.1" + CRLF
+ + "accept: */*,application/atom+xml,application/atomsvc+xml,application/xml" + CRLF
+ + CRLF + CRLF
+ + "--batch_8194-cf13-1f56" + CRLF
+ + "Content-Type: multipart/mixed; boundary=changeset_f980-1cb6-94dd" + CRLF
+ + CRLF
+ + "--changeset_f980-1cb6-94dd" + CRLF
+ + MIME_HEADERS
+ + "Content-Id: 1" + CRLF
+ + CRLF
+ + "POST Employees HTTP/1.1" + CRLF
+ + "Content-type: application/octet-stream" + CRLF
+ + CRLF
+ + "/9j/4AAQSkZJRgABAQEBLAEsAAD/4RM0RXhpZgAATU0AKgAAAAgABwESAAMAAAABAAEAAAEaAAUAAAABAAAAYgEbAAUAAAA" + CRLF
+ + CRLF
+ + "--changeset_f980-1cb6-94dd" + CRLF
+ + MIME_HEADERS
+ + "Content-Id: 1" + CRLF
+ + CRLF
+ + "PUT $1/EmployeeName HTTP/1.1" + CRLF
+ + "Content-Type: application/json;odata=verbose" + CRLF
+ + CRLF
+ + "{\"EmployeeName\":\"Peter Fall\"}" + CRLF
+ + "--changeset_f980-1cb6-94dd--" + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parse(batch);
+ }
+
+ @Test
+ public void testPreamble() throws BatchException, IOException {
+ final String batch = ""
+ + "This is a preamble and must be ignored" + CRLF
+ + CRLF
+ + CRLF
+ + "----1242" + CRLF
+ + "--batch_8194-cf13-1f56" + CRLF
+ + MIME_HEADERS
+ + CRLF
+ + "GET Employees HTTP/1.1" + CRLF
+ + "accept: */*,application/atom+xml,application/atomsvc+xml,application/xml" + CRLF
+ + "Content-Id: BBB" + CRLF
+ + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56" + CRLF
+ + "Content-Type: multipart/mixed; boundary=changeset_f980-1cb6-94dd" + CRLF
+ + CRLF
+ + "This is a preamble and must be ignored" + CRLF
+ + CRLF
+ + CRLF
+ + "----1242" + CRLF
+ + "--changeset_f980-1cb6-94dd" + CRLF
+ + MIME_HEADERS
+ + "Content-Id: 1" + CRLF
+ + CRLF
+ + "POST Employees HTTP/1.1" + CRLF
+ + "Content-type: application/octet-stream" + CRLF
+ + CRLF
+ + "/9j/4AAQSkZJRgABAQEBLAEsAAD/4RM0RXhpZgAATU0AKgAAAAgABwESAAMAAAABAAEAAAEaAAUAAAABAAAAYgEbAAUAAAA" + CRLF
+ + CRLF
+ + "--changeset_f980-1cb6-94dd" + CRLF
+ + MIME_HEADERS
+ + "Content-ID: 2" + CRLF
+ + CRLF
+ + "PUT $1/EmployeeName HTTP/1.1" + CRLF
+ + "Content-Type: application/json;odata=verbose" + CRLF
+ + CRLF
+ + "{\"EmployeeName\":\"Peter Fall\"}" + CRLF
+ + "--changeset_f980-1cb6-94dd--" + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+ final List<BatchRequestPart> batchRequestParts = parse(batch);
+
+ assertNotNull(batchRequestParts);
+ assertEquals(2, batchRequestParts.size());
+
+ final BatchRequestPart getRequestPart = batchRequestParts.get(0);
+ assertEquals(1, getRequestPart.getRequests().size());
+
+ final ODataRequest getRequest = getRequestPart.getRequests().get(0);
+ assertEquals(HttpMethod.GET, getRequest.getMethod());
+
+ final BatchRequestPart changeSetPart = batchRequestParts.get(1);
+ assertEquals(2, changeSetPart.getRequests().size());
+ assertEquals("/9j/4AAQSkZJRgABAQEBLAEsAAD/4RM0RXhpZgAATU0AKgAAAAgABwESAAMAAAABAAEAAAEaAAUAAAABAAAAYgEbAAUAAAA"
+ + CRLF,
+ StringUtil.toString(changeSetPart.getRequests().get(0).getBody()));
+ assertEquals("{\"EmployeeName\":\"Peter Fall\"}",
+ StringUtil.toString(changeSetPart.getRequests().get(1).getBody()));
+ }
+
+ @Test
+ public void testContentTypeCaseInsensitive() throws BatchException, IOException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + "Content-Type: muLTiParT/mixed; boundary=changeset_f980-1cb6-94dd" + CRLF
+ + CRLF
+ + "--changeset_f980-1cb6-94dd" + CRLF
+ + MIME_HEADERS
+ + "Content-ID: 1" + CRLF
+ + "Content-Length: 200" + CRLF
+ + CRLF
+ + "PUT EmployeeName HTTP/1.1" + CRLF
+ + "Content-Type: application/json;odata=verbose" + CRLF
+ + CRLF
+ + "{\"EmployeeName\":\"Peter Fall\"}" + CRLF
+ + "--changeset_f980-1cb6-94dd--" + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parse(batch);
+ }
+
+ @Test
+ public void testContentTypeBoundaryCaseInsensitive() throws BatchException, IOException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + "Content-Type: multipart/mixed; bOunDaRy=changeset_f980-1cb6-94dd" + CRLF
+ + CRLF
+ + "--changeset_f980-1cb6-94dd" + CRLF
+ + MIME_HEADERS
+ + "Content-ID: 1" + CRLF
+ + CRLF
+ + "PUT EmployeeName HTTP/1.1" + CRLF
+ + "Content-Type: application/json;odata=verbose" + CRLF
+ + CRLF
+ + "{\"EmployeeName\":\"Peter Fall\"}" + CRLF
+ + "--changeset_f980-1cb6-94dd--" + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+ final List<BatchRequestPart> batchRequestParts = parse(batch);
+
+ assertNotNull(batchRequestParts);
+ assertEquals(1, batchRequestParts.size());
+ assertTrue(batchRequestParts.get(0).isChangeSet());
+ assertEquals(1, batchRequestParts.get(0).getRequests().size());
+ }
+
+ @Test
+ public void testEpilog() throws BatchException, IOException {
+ String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + MIME_HEADERS
+ + CRLF
+ + "GET Employees HTTP/1.1" + CRLF
+ + "accept: */*,application/atom+xml,application/atomsvc+xml,application/xml" + CRLF
+ + "Content-Id: BBB" + CRLF
+ + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56" + CRLF
+ + "Content-Type: multipart/mixed; boundary=changeset_f980-1cb6-94dd" + CRLF
+ + CRLF
+ + "--changeset_f980-1cb6-94dd" + CRLF
+ + MIME_HEADERS
+ + "Content-Id: 1" + CRLF
+ + CRLF
+ + "POST Employees HTTP/1.1" + CRLF
+ + "Content-type: application/octet-stream" + CRLF
+ + CRLF
+ + "/9j/4AAQSkZJRgABAQEBLAEsAAD/4RM0RXhpZgAATU0AKgAAAAgABwESAAMAAAABAAEAAAEaAAUAAAABAAAAYgEbAAUAAAA" + CRLF
+ + CRLF
+ + "--changeset_f980-1cb6-94dd" + CRLF
+ + MIME_HEADERS
+ + "Content-ID: 2" + CRLF
+ + CRLF
+ + "PUT $1/EmployeeName HTTP/1.1" + CRLF
+ + "Content-Type: application/json;odata=verbose" + CRLF
+ + CRLF
+ + "{\"EmployeeName\":\"Peter Fall\"}" + CRLF
+ + "--changeset_f980-1cb6-94dd--" + CRLF
+ + CRLF
+ + "This is an epilog and must be ignored" + CRLF
+ + CRLF
+ + CRLF
+ + "----1242"
+ + CRLF
+ + "--batch_8194-cf13-1f56--"
+ + CRLF
+ + "This is an epilog and must be ignored" + CRLF
+ + CRLF
+ + CRLF
+ + "----1242";
+ final List<BatchRequestPart> batchRequestParts = parse(batch);
+
+ assertNotNull(batchRequestParts);
+ assertEquals(2, batchRequestParts.size());
+
+ BatchRequestPart getRequestPart = batchRequestParts.get(0);
+ assertEquals(1, getRequestPart.getRequests().size());
+ ODataRequest getRequest = getRequestPart.getRequests().get(0);
+ assertEquals(HttpMethod.GET, getRequest.getMethod());
+
+ BatchRequestPart changeSetPart = batchRequestParts.get(1);
+ assertEquals(2, changeSetPart.getRequests().size());
+ assertEquals("/9j/4AAQSkZJRgABAQEBLAEsAAD/4RM0RXhpZgAATU0AKgAAAAgABwESAAMAAAABAAEAAAEaAAUAAAABAAAAYgEbAAUAAAA"
+ + CRLF,
+ StringUtil.toString(changeSetPart.getRequests().get(0).getBody()));
+ assertEquals("{\"EmployeeName\":\"Peter Fall\"}",
+ StringUtil.toString(changeSetPart.getRequests().get(1).getBody()));
+ }
+
+ @Test
+ public void testLargeBatch() throws BatchException, IOException {
+ final InputStream in = readFile("/batchLarge.batch");
+ parse(in);
+ }
+
+ @Test
+ public void testForddenHeaderAuthorisation() throws UnsupportedEncodingException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + MIME_HEADERS
+ + CRLF
+ + "GET Employees('1')/EmployeeName HTTP/1.1" + CRLF
+ + "Authorization: Basic QWxhZdsdsddsduIHNlc2FtZQ==" + CRLF
+ + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, MessageKeys.FORBIDDEN_HEADER);
+ }
+
+ @Test
+ public void testForddenHeaderExpect() throws UnsupportedEncodingException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + MIME_HEADERS
+ + CRLF
+ + "GET Employees('1')/EmployeeName HTTP/1.1" + CRLF
+ + "Expect: 100-continue" + CRLF
+ + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, MessageKeys.FORBIDDEN_HEADER);
+ }
+
+ @Test
+ public void testForddenHeaderFrom() throws UnsupportedEncodingException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + MIME_HEADERS
+ + CRLF
+ + "GET Employees('1')/EmployeeName HTTP/1.1" + CRLF
+ + "From: test@test.com" + CRLF
+ + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, MessageKeys.FORBIDDEN_HEADER);
+ }
+
+ @Test
+ public void testForddenHeaderRange() throws UnsupportedEncodingException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + MIME_HEADERS
+ + CRLF
+ + "GET Employees('1')/EmployeeName HTTP/1.1" + CRLF
+ + "Range: 200-256" + CRLF
+ + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, MessageKeys.FORBIDDEN_HEADER);
+ }
+
+ @Test
+ public void testForddenHeaderMaxForwards() throws UnsupportedEncodingException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + MIME_HEADERS
+ + CRLF
+ + "GET Employees('1')/EmployeeName HTTP/1.1" + CRLF
+ + "Max-Forwards: 3" + CRLF
+ + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, MessageKeys.FORBIDDEN_HEADER);
+ }
+
+ @Test
+ public void testForddenHeaderTE() throws UnsupportedEncodingException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56" + CRLF
+ + MIME_HEADERS
+ + CRLF
+ + "GET Employees('1')/EmployeeName HTTP/1.1" + CRLF
+ + "TE: deflate" + CRLF
+ + CRLF
+ + CRLF
+ + "--batch_8194-cf13-1f56--";
+
+ parseInvalidBatchBody(batch, MessageKeys.FORBIDDEN_HEADER);
+ }
+
+ private List<BatchRequestPart> parse(final InputStream in, final boolean isStrict) throws BatchException {
+ final BatchParser parser = new BatchParser(CONTENT_TYPE, SERVICE_ROOT, "", isStrict);
+ final List<BatchRequestPart> batchRequestParts = parser.parseBatchRequest(in);
+
+ assertNotNull(batchRequestParts);
+ assertFalse(batchRequestParts.isEmpty());
+
+ return batchRequestParts;
+ }
+
+ private List<BatchRequestPart> parse(final InputStream in) throws BatchException {
+ return parse(in, true);
+ }
+
+ private List<BatchRequestPart> parse(final String batch) throws BatchException, UnsupportedEncodingException {
+ return parse(batch, true);
+ }
+
+ private List<BatchRequestPart> parse(final String batch, final boolean isStrict) throws BatchException,
+ UnsupportedEncodingException {
+ return parse(StringUtil.toInputStream(batch), isStrict);
+ }
+
+ private void parseInvalidBatchBody(final String batch, final MessageKeys key, final boolean isStrict)
+ throws UnsupportedEncodingException {
+ final BatchParser parser = new BatchParser(CONTENT_TYPE, SERVICE_ROOT, "", isStrict);
+
+ try {
+ parser.parseBatchRequest(StringUtil.toInputStream(batch));
+ fail("No exception thrown. Expect: " + key.toString());
+ } catch (BatchException e) {
+ assertMessageKey(e, key);
+ }
+ }
+
+ private void parseInvalidBatchBody(final String batch, final MessageKeys key) throws UnsupportedEncodingException {
+ parseInvalidBatchBody(batch, key, true);
+ }
+
+ private void assertMessageKey(final BatchException e, final MessageKeys key) {
+ assertEquals(key, e.getMessageKey());
+ }
+
+ private InputStream readFile(final String fileName) throws IOException {
+ final InputStream in = ClassLoader.class.getResourceAsStream(fileName);
+ if (in == null) {
+ throw new IOException("Requested file '" + fileName + "' was not found.");
+ }
+ return in;
+ }
+}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/32247295/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/parser/BatchParserCommonTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/parser/BatchParserCommonTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/parser/BatchParserCommonTest.java
new file mode 100644
index 0000000..6fb0796
--- /dev/null
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/parser/BatchParserCommonTest.java
@@ -0,0 +1,230 @@
+/*
+ * 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.batch.parser;
+
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.olingo.commons.api.http.HttpHeader;
+import org.apache.olingo.server.core.batch.BatchException;
+import org.apache.olingo.server.core.batch.parser.BatchParserCommon;
+import org.apache.olingo.server.core.batch.parser.BufferedReaderIncludingLineEndings.Line;
+import org.apache.olingo.server.core.batch.parser.Header;
+import org.junit.Test;
+
+public class BatchParserCommonTest {
+
+ private static final String CRLF = "\r\n";
+
+ @Test
+ public void testMultipleHeader() throws BatchException {
+ String[] messageRaw = new String[] {
+ "Content-Id: 1" + CRLF,
+ "Content-Id: 2" + CRLF,
+ "content-type: Application/http" + CRLF,
+ "content-transfer-encoding: Binary" + CRLF
+ };
+ List<Line> message = toLineList(messageRaw);
+
+ final Header header = BatchParserCommon.consumeHeaders(message);
+ assertNotNull(header);
+
+ final List<String> contentIdHeaders = header.getHeaders(BatchParserCommon.HTTP_CONTENT_ID);
+ assertNotNull(contentIdHeaders);
+ assertEquals(2, contentIdHeaders.size());
+ assertEquals("1", contentIdHeaders.get(0));
+ assertEquals("2", contentIdHeaders.get(1));
+ }
+
+ @Test
+ public void testMultipleHeaderSameValue() throws BatchException {
+ String[] messageRaw = new String[] {
+ "Content-Id: 1" + CRLF,
+ "Content-Id: 1" + CRLF,
+ "content-type: Application/http" + CRLF,
+ "content-transfer-encoding: Binary" + CRLF
+ };
+ List<Line> message = toLineList(messageRaw);
+
+ final Header header = BatchParserCommon.consumeHeaders(message);
+ assertNotNull(header);
+
+ final List<String> contentIdHeaders = header.getHeaders(BatchParserCommon.HTTP_CONTENT_ID);
+ assertNotNull(contentIdHeaders);
+ assertEquals(1, contentIdHeaders.size());
+ assertEquals("1", contentIdHeaders.get(0));
+ }
+
+ @Test
+ public void testHeaderSperatedByComma() throws BatchException {
+ String[] messageRaw = new String[] {
+ "Content-Id: 1" + CRLF,
+ "Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11" + CRLF,
+ "content-type: Application/http" + CRLF,
+ "content-transfer-encoding: Binary" + CRLF
+ };
+ List<Line> message = toLineList(messageRaw);
+
+ final Header header = BatchParserCommon.consumeHeaders(message);
+ assertNotNull(header);
+
+ final List<String> upgradeHeader = header.getHeaders("upgrade");
+ assertNotNull(upgradeHeader);
+ assertEquals(4, upgradeHeader.size());
+ assertEquals("HTTP/2.0", upgradeHeader.get(0));
+ assertEquals("SHTTP/1.3", upgradeHeader.get(1));
+ assertEquals("IRC/6.9", upgradeHeader.get(2));
+ assertEquals("RTA/x11", upgradeHeader.get(3));
+ }
+
+ @Test
+ public void testMultipleAcceptHeader() throws BatchException {
+ String[] messageRaw = new String[] {
+ "Accept: application/atomsvc+xml;q=0.8, application/json;odata=verbose;q=0.5, */*;q=0.1" + CRLF,
+ "Accept: text/plain;q=0.3" + CRLF,
+ "Accept-Language:en-US,en;q=0.7,en-UK;q=0.9" + CRLF,
+ "content-type: Application/http" + CRLF,
+ "content-transfer-encoding: Binary" + CRLF
+ };
+ List<Line> message = toLineList(messageRaw);
+
+ final Header header = BatchParserCommon.consumeHeaders(message);
+ assertNotNull(header);
+
+ final List<String> acceptHeader = header.getHeaders(HttpHeader.ACCEPT);
+ assertNotNull(acceptHeader);
+ assertEquals(4, acceptHeader.size());
+ }
+
+ @Test
+ public void testMultipleAcceptHeaderSameValue() throws BatchException {
+ String[] messageRaw = new String[] {
+ "Accept: application/atomsvc+xml;q=0.8, application/json;odata=verbose;q=0.5, */*;q=0.1" + CRLF,
+ "Accept: application/atomsvc+xml;q=0.8" + CRLF,
+ "Accept-Language:en-US,en;q=0.7,en-UK;q=0.9" + CRLF,
+ "content-type: Application/http" + CRLF,
+ "content-transfer-encoding: Binary" + CRLF
+ };
+ List<Line> message = toLineList(messageRaw);
+
+ final Header header = BatchParserCommon.consumeHeaders(message);
+ assertNotNull(header);
+
+ final List<String> acceptHeader = header.getHeaders(HttpHeader.ACCEPT);
+ assertNotNull(acceptHeader);
+ assertEquals(3, acceptHeader.size());
+ }
+
+ @Test
+ public void testMultipleAccepLanguagetHeader() throws BatchException {
+ String[] messageRaw = new String[] {
+ "Accept-Language:en-US,en;q=0.7,en-UK;q=0.9" + CRLF,
+ "Accept-Language: de-DE;q=0.3" + CRLF,
+ "content-type: Application/http" + CRLF,
+ "content-transfer-encoding: Binary" + CRLF
+ };
+ List<Line> message = toLineList(messageRaw);
+
+ final Header header = BatchParserCommon.consumeHeaders(message);
+ assertNotNull(header);
+
+ final List<String> acceptLanguageHeader = header.getHeaders(HttpHeader.ACCEPT_LANGUAGE);
+ assertNotNull(acceptLanguageHeader);
+ assertEquals(4, acceptLanguageHeader.size());
+ }
+
+ @Test
+ public void testMultipleAccepLanguagetHeaderSameValue() throws BatchException {
+ String[] messageRaw = new String[] {
+ "Accept-Language:en-US,en;q=0.7,en-UK;q=0.9" + CRLF,
+ "Accept-Language:en-US,en;q=0.7" + CRLF,
+ "content-type: Application/http" + CRLF,
+ "content-transfer-encoding: Binary" + CRLF
+ };
+ List<Line> message = toLineList(messageRaw);
+
+ final Header header = BatchParserCommon.consumeHeaders(message);
+ assertNotNull(header);
+
+ final List<String> acceptLanguageHeader = header.getHeaders(HttpHeader.ACCEPT_LANGUAGE);
+ assertNotNull(acceptLanguageHeader);
+ assertEquals(3, acceptLanguageHeader.size());
+ }
+
+ @Test
+ public void testRemoveEndingCRLF() {
+ String line = "Test\r\n";
+ assertEquals("Test", BatchParserCommon.removeEndingCRLF(new Line(line,1)).toString());
+ }
+
+ @Test
+ public void testRemoveLastEndingCRLF() {
+ String line = "Test\r\n\r\n";
+ assertEquals("Test\r\n", BatchParserCommon.removeEndingCRLF(new Line(line,1)).toString());
+ }
+
+ @Test
+ public void testRemoveEndingCRLFWithWS() {
+ String line = "Test\r\n ";
+ assertEquals("Test", BatchParserCommon.removeEndingCRLF(new Line(line,1)).toString());
+ }
+
+ @Test
+ public void testRemoveEndingCRLFNothingToRemove() {
+ String line = "Hallo\r\nBla";
+ assertEquals("Hallo\r\nBla", BatchParserCommon.removeEndingCRLF(new Line(line,1)).toString());
+ }
+
+ @Test
+ public void testRemoveEndingCRLFAll() {
+ String line = "\r\n";
+ assertEquals("", BatchParserCommon.removeEndingCRLF(new Line(line,1)).toString());
+ }
+
+ @Test
+ public void testRemoveEndingCRLFSpace() {
+ String line = "\r\n ";
+ assertEquals("", BatchParserCommon.removeEndingCRLF(new Line(line,1)).toString());
+ }
+
+ @Test
+ public void testRemoveLastEndingCRLFWithWS() {
+ String line = "Test \r\n";
+ assertEquals("Test ", BatchParserCommon.removeEndingCRLF(new Line(line,1)).toString());
+ }
+
+ @Test
+ public void testRemoveLastEndingCRLFWithWSLong() {
+ String line = "Test \r\nTest2 \r\n";
+ assertEquals("Test \r\nTest2 ", BatchParserCommon.removeEndingCRLF(new Line(line,1)).toString());
+ }
+
+ private List<Line> toLineList(String[] messageRaw) {
+ final List<Line> lineList = new ArrayList<Line>();
+ int counter = 1;
+
+ for(final String currentLine : messageRaw) {
+ lineList.add(new Line(currentLine, counter++));
+ }
+
+ return lineList;
+ }
+}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/32247295/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/parser/BufferedReaderIncludingLineEndingsTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/parser/BufferedReaderIncludingLineEndingsTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/parser/BufferedReaderIncludingLineEndingsTest.java
new file mode 100644
index 0000000..eac9dff
--- /dev/null
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/parser/BufferedReaderIncludingLineEndingsTest.java
@@ -0,0 +1,484 @@
+/*
+ * 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.batch.parser;
+
+import static org.junit.Assert.*;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+
+import org.apache.olingo.server.core.batch.parser.BufferedReaderIncludingLineEndings;
+import org.apache.olingo.server.core.batch.parser.BufferedReaderIncludingLineEndings.Line;
+import org.junit.Test;
+
+public class BufferedReaderIncludingLineEndingsTest {
+
+
+ private static final String TEXT_COMBINED = "Test\r" +
+ "Test2\r\n" +
+ "Test3\n" +
+ "Test4\r" +
+ "\r" +
+ "\r\n" +
+ "\r\n" +
+ "Test5\n" +
+ "Test6\r\n" +
+ "Test7\n" +
+ "\n";
+
+ private static final String TEXT_SMALL = "Test\r" +
+ "123";
+ private static final String TEXT_EMPTY = "";
+
+ @Test
+ public void testSimpleText() throws IOException {
+ final String TEXT = "Test";
+ BufferedReaderIncludingLineEndings reader = create(TEXT);
+
+ assertEquals(TEXT, reader.readLine());
+ assertNull(reader.readLine());
+ assertNull(reader.readLine());
+ reader.close();
+ }
+
+ @Test
+ public void testNoText() throws IOException {
+ final String TEXT = "";
+ BufferedReaderIncludingLineEndings reader = create(TEXT);
+
+ assertNull(reader.readLine());
+ assertNull(reader.readLine());
+ reader.close();
+ }
+
+ @Test
+ public void testNoBytes() throws IOException {
+ BufferedReaderIncludingLineEndings reader =
+ new BufferedReaderIncludingLineEndings(new InputStreamReader(new ByteArrayInputStream(new byte[0])));
+
+ assertNull(reader.readLine());
+ assertNull(reader.readLine());
+ reader.close();
+ }
+
+ @Test
+ public void testCRLF() throws IOException {
+ final String TEXT = "Test\r\n" +
+ "Test2";
+
+ BufferedReaderIncludingLineEndings reader = create(TEXT);
+
+ assertEquals("Test\r\n", reader.readLine());
+ assertEquals("Test2", reader.readLine());
+ assertNull(reader.readLine());
+ assertNull(reader.readLine());
+ reader.close();
+ }
+
+ @Test
+ public void testLF() throws IOException {
+ final String TEXT = "Test\n" +
+ "Test2";
+
+ BufferedReaderIncludingLineEndings reader = create(TEXT);
+
+ assertEquals("Test\n", reader.readLine());
+ assertEquals("Test2", reader.readLine());
+ assertNull(reader.readLine());
+ assertNull(reader.readLine());
+ reader.close();
+ }
+
+ @Test
+ public void testCR() throws IOException {
+ final String TEXT = "Test\r" +
+ "Test2";
+
+ BufferedReaderIncludingLineEndings reader = create(TEXT);
+
+ assertEquals("Test\r", reader.readLine());
+ assertEquals("Test2", reader.readLine());
+ assertNull(reader.readLine());
+ assertNull(reader.readLine());
+ reader.close();
+ }
+
+ @Test
+ public void testCombined() throws IOException {
+ BufferedReaderIncludingLineEndings reader = create(TEXT_COMBINED);
+
+ assertEquals("Test\r", reader.readLine());
+ assertEquals("Test2\r\n", reader.readLine());
+ assertEquals("Test3\n", reader.readLine());
+ assertEquals("Test4\r", reader.readLine());
+ assertEquals("\r", reader.readLine());
+ assertEquals("\r\n", reader.readLine());
+ assertEquals("\r\n", reader.readLine());
+ assertEquals("Test5\n", reader.readLine());
+ assertEquals("Test6\r\n", reader.readLine());
+ assertEquals("Test7\n", reader.readLine());
+ assertEquals("\n", reader.readLine());
+ assertNull(reader.readLine());
+ assertNull(reader.readLine());
+ reader.close();
+ }
+
+ @Test
+ public void testCombinedBufferSizeTwo() throws IOException {
+ BufferedReaderIncludingLineEndings reader = create(TEXT_COMBINED, 2);
+
+ assertEquals("Test\r", reader.readLine());
+ assertEquals("Test2\r\n", reader.readLine());
+ assertEquals("Test3\n", reader.readLine());
+ assertEquals("Test4\r", reader.readLine());
+ assertEquals("\r", reader.readLine());
+ assertEquals("\r\n", reader.readLine());
+ assertEquals("\r\n", reader.readLine());
+ assertEquals("Test5\n", reader.readLine());
+ assertEquals("Test6\r\n", reader.readLine());
+ assertEquals("Test7\n", reader.readLine());
+ assertEquals("\n", reader.readLine());
+ assertNull(reader.readLine());
+ assertNull(reader.readLine());
+ reader.close();
+ }
+
+ @Test
+ public void testCombinedBufferSizeOne() throws IOException {
+ 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";
+
+ BufferedReaderIncludingLineEndings reader = create(TEXT, 1);
+
+ assertEquals("Test\r", reader.readLine());
+ assertEquals("Test2\r\n", reader.readLine());
+ assertEquals("Test3\n", reader.readLine());
+ assertEquals("Test4\r", reader.readLine());
+ assertEquals("\r", reader.readLine());
+ assertEquals("\r\n", reader.readLine());
+ assertEquals("\r\n", reader.readLine());
+ assertEquals("Test5\n", reader.readLine());
+ assertEquals("Test6\r\n", reader.readLine());
+ assertEquals("Test7\n", reader.readLine());
+ assertEquals("\r\n", reader.readLine());
+ assertNull(reader.readLine());
+ assertNull(reader.readLine());
+
+ reader.close();
+ }
+
+ @Test
+ public void testDoubleLF() throws IOException {
+ final String TEXT = "Test\r" +
+ "\r";
+
+ BufferedReaderIncludingLineEndings reader = create(TEXT, 1);
+
+ assertEquals("Test\r", reader.readLine());
+ assertEquals("\r", reader.readLine());
+ reader.close();
+ }
+
+ @Test
+ public void testSkipSimple() throws IOException {
+ BufferedReaderIncludingLineEndings reader = create(TEXT_SMALL);
+
+ assertEquals(5, reader.skip(5)); // Test\r
+ assertEquals("123", reader.readLine());
+ assertNull(reader.readLine());
+ assertNull(reader.readLine());
+ reader.close();
+ }
+
+ @Test
+ public void testSkipBufferOne() throws IOException {
+ BufferedReaderIncludingLineEndings reader = create(TEXT_SMALL, 1);
+
+ assertEquals(5, reader.skip(5)); // Test\r
+ assertEquals("123", reader.readLine());
+ assertNull(reader.readLine());
+ assertNull(reader.readLine());
+ reader.close();
+ }
+
+ @Test
+ public void testReadThanSkip() throws IOException {
+ final String TEXT = "Test\r" +
+ "\r" +
+ "123";
+
+ BufferedReaderIncludingLineEndings reader = create(TEXT);
+
+ assertEquals("Test\r", reader.readLine());
+ assertEquals(1, reader.skip(1)); // Test\r
+ assertEquals("123", reader.readLine());
+ assertNull(reader.readLine());
+ assertNull(reader.readLine());
+ reader.close();
+ }
+
+ @Test
+ public void testReadMoreBufferCapacityThanCharacterAvailable() throws IOException {
+ final String TEXT = "Foo";
+ char[] buffer = new char[20];
+
+ BufferedReaderIncludingLineEndings reader = create(TEXT);
+ assertEquals(3, reader.read(buffer, 0, 20));
+ assertEquals(-1, reader.read(buffer, 0, 20));
+ reader.close();
+
+ BufferedReaderIncludingLineEndings readerBufferOne = create(TEXT, 1);
+ assertEquals(3, readerBufferOne.read(buffer, 0, 20));
+ assertEquals(-1, readerBufferOne.read(buffer, 0, 20));
+ readerBufferOne.close();
+ }
+
+ @Test
+ public void testSkipZero() throws IOException {
+ final String TEXT = "Test\r" +
+ "123\r\n";
+
+ BufferedReaderIncludingLineEndings reader = create(TEXT);
+
+ assertEquals(0, reader.skip(0)); // Test\r
+ assertEquals("Test\r", reader.readLine());
+ assertEquals("123\r\n", reader.readLine());
+ assertNull(reader.readLine());
+ assertNull(reader.readLine());
+ reader.close();
+ }
+
+ @Test
+ public void testSkipToMuch() throws IOException {
+ BufferedReaderIncludingLineEndings reader = create(TEXT_SMALL);
+
+ assertEquals(8, reader.skip(10)); // Test\r
+ assertEquals(null, reader.readLine());
+ reader.close();
+ }
+
+ @Test
+ public void testReadBufferOne() throws IOException {
+ BufferedReaderIncludingLineEndings reader = create(TEXT_SMALL, 1);
+
+ assertEquals('T', reader.read());
+ assertEquals('e', reader.read());
+ assertEquals('s', reader.read());
+ assertEquals('t', reader.read());
+ assertEquals('\r', reader.read());
+ assertEquals('1', reader.read());
+ assertEquals('2', reader.read());
+ assertEquals('3', reader.read());
+ assertEquals(-1, reader.read());
+ assertEquals(-1, reader.read());
+ }
+
+ @Test
+ public void testReadZeroBytes() throws IOException {
+ BufferedReaderIncludingLineEndings reader = create(TEXT_SMALL, 1);
+
+ char[] buffer = new char[3];
+ assertEquals(0, reader.read(buffer, 0, 0));
+ assertEquals('T', reader.read());
+ assertEquals(0, reader.read(buffer, 0, 0));
+ assertEquals("est\r", reader.readLine());
+ assertEquals("123", reader.readLine());
+
+ reader.close();
+ }
+
+ @Test
+ public void testRead() throws IOException {
+ BufferedReaderIncludingLineEndings reader = create(TEXT_SMALL);
+
+ assertEquals('T', reader.read());
+ assertEquals('e', reader.read());
+ assertEquals('s', reader.read());
+ assertEquals('t', reader.read());
+ assertEquals('\r', reader.read());
+ assertEquals('1', reader.read());
+ assertEquals('2', reader.read());
+ assertEquals('3', reader.read());
+ assertEquals(-1, reader.read());
+ assertEquals(-1, reader.read());
+ }
+
+ @Test(expected = IndexOutOfBoundsException.class)
+ public void testFailReadBufferAndOffsetBiggerThanBuffer() throws IOException {
+ BufferedReaderIncludingLineEndings reader = create("");
+
+ final char[] buffer = new char[3];
+ reader.read(buffer, 1, 3);
+ }
+
+ @Test(expected = IndexOutOfBoundsException.class)
+ public void testFailLengthNegative() throws IOException {
+ final char[] buffer = new char[3];
+ BufferedReaderIncludingLineEndings reader = create("123");
+
+ reader.read(buffer, 1, -2);
+ reader.close();
+ }
+
+ @Test(expected = IndexOutOfBoundsException.class)
+ public void testFailOffsetNegative() throws IOException {
+ final char[] buffer = new char[3];
+ BufferedReaderIncludingLineEndings reader = create("123");
+
+ reader.read(buffer, -1, 2);
+ reader.close();
+ }
+
+ @Test
+ public void testReadAndReadLine() throws IOException {
+ final String TEXT = "Test\r" +
+ "bar\n" +
+ "123\r\n" +
+ "foo";
+
+ BufferedReaderIncludingLineEndings reader = create(TEXT);
+
+ assertEquals('T', reader.read());
+ assertEquals('e', reader.read());
+ assertEquals('s', reader.read());
+ assertEquals('t', reader.read());
+ assertEquals("\r", reader.readLine());
+ assertEquals("bar\n", reader.readLine());
+ assertEquals('1', reader.read());
+ assertEquals('2', reader.read());
+ assertEquals("3\r\n", reader.readLine());
+ assertEquals("foo", reader.readLine());
+ assertEquals(null, reader.readLine());
+ assertEquals(-1, reader.read());
+ }
+
+ @Test
+ public void testLineEqualsAndHashCode() {
+ Line l1 = new Line("The first line", 1);
+ Line l2 = new Line("The first line", 1);
+ Line l3 = new Line("The second line", 2);
+
+ assertEquals(l1, l2);
+ assertFalse(l1.equals(l3));
+ assertTrue(l1.hashCode() != l3.hashCode());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSkipNegative() throws IOException {
+ BufferedReaderIncludingLineEndings reader = create("123");
+ reader.skip(-1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testFailBufferSizeZero() throws IOException {
+ BufferedReaderIncludingLineEndings reader = create(TEXT_EMPTY, 0);
+ reader.close();
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testInputStreamIsNull() throws IOException {
+ // Same behaviour like BufferedReader
+ BufferedReaderIncludingLineEndings reader = new BufferedReaderIncludingLineEndings(null);
+ reader.close();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testFailBufferSizeNegative() throws IOException {
+ BufferedReaderIncludingLineEndings reader = create(TEXT_EMPTY, -1);
+ reader.close();
+ }
+
+ @Test
+ public void testMarkSupoorted() throws IOException {
+ BufferedReaderIncludingLineEndings reader = create(TEXT_EMPTY);
+
+ assertEquals(false, reader.markSupported());
+ reader.close();
+ }
+
+ @Test(expected = IOException.class)
+ public void testFailMark() throws IOException {
+ BufferedReaderIncludingLineEndings reader = create("123");
+
+ reader.mark(1);
+ }
+
+ @Test(expected = IOException.class)
+ public void testFailReset() throws IOException {
+ BufferedReaderIncludingLineEndings reader = create("123");
+
+ reader.reset();
+ }
+
+ @Test
+ public void testReady() throws IOException {
+ BufferedReaderIncludingLineEndings reader = create("123\r123");
+ assertEquals(false, reader.ready());
+ assertEquals("123\r", reader.readLine());
+ assertEquals(true, reader.ready());
+ assertEquals("123", reader.readLine());
+ assertEquals(false, reader.ready());
+
+ reader.close();
+ }
+
+ @Test
+ public void testToList() throws IOException {
+ BufferedReaderIncludingLineEndings reader = create(TEXT_COMBINED);
+ List<Line> stringList = reader.toLineList();
+
+ assertEquals(11, stringList.size());
+ assertEquals("Test\r", stringList.get(0).toString());
+ assertEquals("Test2\r\n", stringList.get(1).toString());
+ assertEquals("Test3\n", stringList.get(2).toString());
+ assertEquals("Test4\r", stringList.get(3).toString());
+ assertEquals("\r", stringList.get(4).toString());
+ assertEquals("\r\n", stringList.get(5).toString());
+ assertEquals("\r\n", stringList.get(6).toString());
+ assertEquals("Test5\n", stringList.get(7).toString());
+ assertEquals("Test6\r\n", stringList.get(8).toString());
+ assertEquals("Test7\n", stringList.get(9).toString());
+ assertEquals("\n", stringList.get(10).toString());
+ reader.close();
+ }
+
+ private BufferedReaderIncludingLineEndings create(final String inputString) throws UnsupportedEncodingException {
+ return new BufferedReaderIncludingLineEndings(new InputStreamReader(new ByteArrayInputStream(inputString
+ .getBytes("UTF-8"))));
+ }
+
+ private BufferedReaderIncludingLineEndings create(final String inputString, int bufferSize)
+ throws UnsupportedEncodingException {
+ return new BufferedReaderIncludingLineEndings(new InputStreamReader(new ByteArrayInputStream(inputString
+ .getBytes("UTF-8"))), bufferSize);
+ }
+
+}