You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by mi...@apache.org on 2014/10/14 20:35:23 UTC

[36/39] git commit: [OLINGO-436] Fix: Validate request HTTP version

[OLINGO-436] Fix: Validate request HTTP version

Signed-off-by: Christian Amend <ch...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata2/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata2/commit/86c4ddc4
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata2/tree/86c4ddc4
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata2/diff/86c4ddc4

Branch: refs/heads/Olingo-129_PocJpaDataStore
Commit: 86c4ddc4205aa29d744ec8b3047fae15541a1493
Parents: e7e4447
Author: Christian Holzer <c....@sap.com>
Authored: Tue Oct 14 13:04:58 2014 +0200
Committer: Christian Amend <ch...@apache.org>
Committed: Tue Oct 14 13:59:48 2014 +0200

----------------------------------------------------------------------
 .../odata2/api/batch/BatchParserResult.java     |   6 +
 .../odata2/core/batch/v2/BatchParserCommon.java | 103 ++--------
 .../batch/v2/BatchRequestTransformator.java     |  75 ++------
 .../batch/v2/BatchResponseTransformator.java    |  34 +---
 .../core/batch/v2/BatchTransformatorCommon.java | 188 ++++++++++++++++++-
 .../core/batch/BatchRequestParserTest.java      |  92 +++++++--
 .../odata2/core/batch/BatchRequestTest.java     |   1 -
 .../BufferedReaderIncludingLineEndingsTest.java |  13 +-
 8 files changed, 322 insertions(+), 190 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/86c4ddc4/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/batch/BatchParserResult.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/batch/BatchParserResult.java b/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/batch/BatchParserResult.java
index 1e2054f..842933d 100644
--- a/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/batch/BatchParserResult.java
+++ b/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/batch/BatchParserResult.java
@@ -18,6 +18,12 @@
  ******************************************************************************/
 package org.apache.olingo.odata2.api.batch;
 
+/**
+ * A BatchParserResult
+ * 
+ * <p> BatchParserResult represents a BatchRequestPart or a BatchResponse part. 
+ * 
+ */
 public interface BatchParserResult {
 
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/86c4ddc4/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchParserCommon.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchParserCommon.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchParserCommon.java
index cac340e..62f03c2 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchParserCommon.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchParserCommon.java
@@ -20,8 +20,6 @@ package org.apache.olingo.odata2.core.batch.v2;
 
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
-import java.net.URI;
-import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -35,10 +33,6 @@ import java.util.regex.Pattern;
 import org.apache.olingo.odata2.api.batch.BatchException;
 import org.apache.olingo.odata2.api.commons.HttpContentType;
 import org.apache.olingo.odata2.api.commons.HttpHeaders;
-import org.apache.olingo.odata2.api.uri.PathInfo;
-import org.apache.olingo.odata2.api.uri.PathSegment;
-import org.apache.olingo.odata2.core.ODataPathSegmentImpl;
-import org.apache.olingo.odata2.core.PathInfoImpl;
 import org.apache.olingo.odata2.core.batch.AcceptParser;
 import org.apache.olingo.odata2.core.batch.v2.BufferedReaderIncludingLineEndings.Line;
 import org.apache.olingo.odata2.core.commons.Decoder;
@@ -58,16 +52,16 @@ public class BatchParserCommon {
   public static final Pattern PATTERN_HEADER_LINE = Pattern.compile("([a-zA-Z\\-]+):\\s?(.*)\\s*");
   public static final Pattern PATTERN_CONTENT_TYPE_APPLICATION_HTTP = Pattern.compile(REG_EX_APPLICATION_HTTP,
       Pattern.CASE_INSENSITIVE);
-  private static final Pattern PATTERN_RELATIVE_URI = Pattern.compile("([^/][^?]*)(\\?.*)?");
+  public static final Pattern PATTERN_RELATIVE_URI = Pattern.compile("([^/][^?]*)(\\?.*)?");
 
   public static String trimLineListToLength(final List<Line> list, final int length) {
-    final String message = stringListToString(list);
+    final String message = lineListToString(list);
     final int lastIndex = Math.min(length, message.length());
 
     return (lastIndex > 0) ? message.substring(0, lastIndex) : "";
   }
 
-  public static String stringListToString(final List<Line> list) {
+  public static String lineListToString(final List<Line> list) {
     StringBuilder builder = new StringBuilder();
 
     for (Line currentLine : list) {
@@ -77,16 +71,16 @@ public class BatchParserCommon {
     return builder.toString();
   }
 
-  public static InputStream convertMessageToInputStream(final List<Line> messageList, final int contentLength)
+  public static InputStream convertLineListToInputStream(final List<Line> messageList, final int contentLength)
       throws BatchException {
     final String message = trimLineListToLength(messageList, contentLength);
 
     return new ByteArrayInputStream(message.getBytes());
   }
 
-  public static InputStream convertMessageToInputStream(final List<Line> messageList)
+  public static InputStream convertLineListToInputStream(final List<Line> messageList)
       throws BatchException {
-    final String message = stringListToString(messageList);
+    final String message = lineListToString(messageList);
 
     return new ByteArrayInputStream(message.getBytes());
   }
@@ -158,14 +152,14 @@ public class BatchParserCommon {
   }
 
   public static Header consumeHeaders(final List<Line> remainingMessage) throws BatchException {
-    final int lineNumberOfHeader = remainingMessage.size() != 0 ? remainingMessage.get(0).getLineNumber() : 0;
-    final Header headers = new Header(lineNumberOfHeader);
-    boolean isHeader = true;
+    final int headerLineNumber = remainingMessage.size() != 0 ? remainingMessage.get(0).getLineNumber() : 0;
+    final Header headers = new Header(headerLineNumber);
     final Iterator<Line> iter = remainingMessage.iterator();
     final AcceptParser acceptParser = new AcceptParser();
     Line currentLine;
     int acceptLineNumber = 0;
     int acceptLanguageLineNumber = 0;
+    boolean isHeader = true;
 
     while (iter.hasNext() && isHeader) {
       currentLine = iter.next();
@@ -224,6 +218,14 @@ public class BatchParserCommon {
     }
   }
 
+  private static String trimQuota(String boundary) {
+    if (boundary.matches("\".*\"")) {
+      boundary = boundary.replace("\"", "");
+    }
+
+    return boundary;
+  }
+  
   public static Map<String, List<String>> parseQueryParameter(final Line httpRequest) {
     Map<String, List<String>> queryParameter = new HashMap<String, List<String>>();
 
@@ -254,75 +256,4 @@ public class BatchParserCommon {
 
     return queryParameter;
   }
-
-  public static PathInfo parseRequestUri(final Line httpStatusLine, final PathInfo batchRequestPathInfo,
-      final String baseUri, final int line)
-          throws BatchException {
-
-    final String odataPathSegmentsAsString;
-    final String queryParametersAsString;
-
-    PathInfoImpl pathInfo = new PathInfoImpl();
-    pathInfo.setServiceRoot(batchRequestPathInfo.getServiceRoot());
-    pathInfo.setPrecedingPathSegment(batchRequestPathInfo.getPrecedingSegments());
-
-    String[] requestParts = httpStatusLine.toString().split(" ");
-    if (requestParts.length == 3) {
-      String uri = requestParts[1];
-      Pattern regexRequestUri;
-
-      try {
-        URI uriObject = new URI(uri);
-        if (uriObject.isAbsolute()) {
-          regexRequestUri = Pattern.compile(baseUri + "/([^/][^?]*)(\\?.*)?");
-        } else {
-          regexRequestUri = PATTERN_RELATIVE_URI;
-
-        }
-
-        Matcher uriParts = regexRequestUri.matcher(uri);
-
-        if (uriParts.lookingAt() && uriParts.groupCount() == 2) {
-          odataPathSegmentsAsString = uriParts.group(1);
-          queryParametersAsString = uriParts.group(2) != null ? uriParts.group(2) : "";
-
-          pathInfo.setODataPathSegment(parseODataPathSegments(odataPathSegmentsAsString));
-          if (!odataPathSegmentsAsString.startsWith("$")) {
-            String requestUri = baseUri + "/" + odataPathSegmentsAsString + queryParametersAsString;
-            pathInfo.setRequestUri(new URI(requestUri));
-          }
-
-        } else {
-          throw new BatchException(BatchException.INVALID_URI.addContent(httpStatusLine.getLineNumber()));
-        }
-
-      } catch (URISyntaxException e) {
-        throw new BatchException(BatchException.INVALID_URI.addContent(line), e);
-      }
-    } else {
-      throw new BatchException(BatchException.INVALID_REQUEST_LINE.addContent(httpStatusLine.toString())
-          .addContent(line));
-    }
-
-    return pathInfo;
-  }
-
-  public static List<PathSegment> parseODataPathSegments(final String odataPathSegmentsAsString) {
-    final List<PathSegment> odataPathSegments = new ArrayList<PathSegment>();
-    final String[] pathParts = odataPathSegmentsAsString.split("/");
-
-    for (final String pathSegment : pathParts) {
-      odataPathSegments.add(new ODataPathSegmentImpl(pathSegment, null));
-    }
-
-    return odataPathSegments;
-  }
-
-  private static String trimQuota(String boundary) {
-    if (boundary.matches("\".*\"")) {
-      boundary = boundary.replace("\"", "");
-    }
-
-    return boundary;
-  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/86c4ddc4/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchRequestTransformator.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchRequestTransformator.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchRequestTransformator.java
index 4faac22..66600b5 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchRequestTransformator.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchRequestTransformator.java
@@ -21,12 +21,9 @@ package org.apache.olingo.odata2.core.batch.v2;
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
-import java.util.Set;
 
 import org.apache.olingo.odata2.api.batch.BatchException;
 import org.apache.olingo.odata2.api.batch.BatchParserResult;
@@ -37,15 +34,11 @@ import org.apache.olingo.odata2.api.processor.ODataRequest.ODataRequestBuilder;
 import org.apache.olingo.odata2.api.uri.PathInfo;
 import org.apache.olingo.odata2.core.batch.BatchHelper;
 import org.apache.olingo.odata2.core.batch.BatchRequestPartImpl;
-import org.apache.olingo.odata2.core.batch.v2.BufferedReaderIncludingLineEndings.Line;
+import org.apache.olingo.odata2.core.batch.v2.BatchTransformatorCommon.HttpRequestStatusLine;
 import org.apache.olingo.odata2.core.batch.v2.Header.HeaderField;
 
 public class BatchRequestTransformator implements BatchTransformator {
 
-  private static final Set<String> HTTP_BATCH_METHODS = new HashSet<String>(Arrays.asList(new String[] { "GET" }));
-  private static final Set<String> HTTP_CHANGE_SET_METHODS = new HashSet<String>(Arrays.asList(new String[] { "POST",
-      "PUT", "DELETE", "MERGE", "PATCH" }));
-
   @Override
   public List<BatchParserResult> transform(final BatchBodyPart bodyPart, final PathInfo pathInfo, final String baseUri)
       throws BatchException {
@@ -89,19 +82,21 @@ public class BatchRequestTransformator implements BatchTransformator {
   private ODataRequest createRequest(final BatchQueryOperation operation, final Header headers,
       final PathInfo pathInfo, final String baseUri, final boolean isChangeSet) throws BatchException {
 
-    final int httpLineNumber = operation.getHttpStatusLine().getLineNumber();
-    ODataHttpMethod httpMethod = getHttpMethod(operation.getHttpStatusLine());
-    validateHttpMethod(httpMethod, isChangeSet, httpLineNumber);
-    validateBody(httpMethod, operation, httpLineNumber);
-    InputStream bodyStrean = getBodyStream(operation, headers, httpMethod);
+    final HttpRequestStatusLine statusLine = new HttpRequestStatusLine( operation.getHttpStatusLine(), 
+                                                                        baseUri, 
+                                                                        pathInfo);
+    statusLine.validateHttpMethod(isChangeSet);
+
+    validateBody(statusLine, operation);
+    InputStream bodyStrean = getBodyStream(operation, headers, statusLine);
 
-    ODataRequestBuilder requestBuilder = ODataRequest.method(httpMethod)
+    ODataRequestBuilder requestBuilder = ODataRequest.method(statusLine.getMethod())
         .acceptableLanguages(getAcceptLanguageHeaders(headers))
         .acceptHeaders(headers.getHeaders(HttpHeaders.ACCEPT))
         .allQueryParameters(BatchParserCommon.parseQueryParameter(operation.getHttpStatusLine()))
         .body(bodyStrean)
         .requestHeaders(headers.toMultiMap())
-        .pathInfo(BatchParserCommon.parseRequestUri(operation.getHttpStatusLine(), pathInfo, baseUri, 0));
+        .pathInfo(statusLine.getPathInfo());
 
     final String contentType = headers.getHeader(HttpHeaders.CONTENT_TYPE);
     if (contentType != null) {
@@ -111,30 +106,32 @@ public class BatchRequestTransformator implements BatchTransformator {
     return requestBuilder.build();
   }
 
-  private void validateBody(final ODataHttpMethod httpStatusLine, final BatchQueryOperation operation, final int line)
+  private void validateBody(final HttpRequestStatusLine httpStatusLine, final BatchQueryOperation operation)
       throws BatchException {
-    if (HTTP_BATCH_METHODS.contains(httpStatusLine.toString()) && isUnvalidGetRequestBody(operation)) {
-      throw new BatchException(BatchException.INVALID_REQUEST_LINE.addContent(httpStatusLine).addContent(line));
+    if (httpStatusLine.getMethod().equals(ODataHttpMethod.GET) && isUnvalidGetRequestBody(operation)) {
+      throw new BatchException(BatchException.INVALID_REQUEST_LINE
+          .addContent(httpStatusLine.getMethod())
+          .addContent(httpStatusLine.getLineNumber()));
     }
   }
 
   private boolean isUnvalidGetRequestBody(final BatchQueryOperation operation) {
     return (operation.getBody().size() > 1)
-        || (operation.getBody().size() == 1 && !operation.getBody().get(0).toString().trim().equals(""));
+        || (operation.getBody().size() == 1 && !"".equals(operation.getBody().get(0).toString().trim()));
   }
 
   private InputStream getBodyStream(final BatchQueryOperation operation, final Header headers,
-      final ODataHttpMethod httpMethod) throws BatchException {
+      final HttpRequestStatusLine httpStatusLine) throws BatchException {
 
-    if (HTTP_BATCH_METHODS.contains(httpMethod.toString())) {
+    if (httpStatusLine.getMethod().equals(ODataHttpMethod.GET)) {
       return new ByteArrayInputStream(new byte[0]);
     } else {
       int contentLength = BatchTransformatorCommon.getContentLength(headers);
 
       if (contentLength == -1) {
-        return BatchParserCommon.convertMessageToInputStream(operation.getBody());
+        return BatchParserCommon.convertLineListToInputStream(operation.getBody());
       } else {
-        return BatchParserCommon.convertMessageToInputStream(operation.getBody(), contentLength);
+        return BatchParserCommon.convertLineListToInputStream(operation.getBody(), contentLength);
       }
     }
   }
@@ -157,19 +154,6 @@ public class BatchRequestTransformator implements BatchTransformator {
     return headers;
   }
 
-  private void validateHttpMethod(final ODataHttpMethod httpMethod, final boolean isChangeSet, final int line)
-      throws BatchException {
-    Set<String> validMethods = (isChangeSet) ? HTTP_CHANGE_SET_METHODS : HTTP_BATCH_METHODS;
-
-    if (!validMethods.contains(httpMethod.toString())) {
-      if (isChangeSet) {
-        throw new BatchException(BatchException.INVALID_CHANGESET_METHOD.addContent(line));
-      } else {
-        throw new BatchException(BatchException.INVALID_QUERY_OPERATION_METHOD.addContent(line));
-      }
-    }
-  }
-
   private List<Locale> getAcceptLanguageHeaders(final Header headers) {
     final List<String> acceptLanguageValues = headers.getHeaders(HttpHeaders.ACCEPT_LANGUAGE);
     List<Locale> acceptLanguages = new ArrayList<Locale>();
@@ -188,23 +172,4 @@ public class BatchRequestTransformator implements BatchTransformator {
     return acceptLanguages;
   }
 
-  private ODataHttpMethod getHttpMethod(final Line httpRequest) throws BatchException {
-    ODataHttpMethod result = null;
-
-    String[] parts = httpRequest.toString().split(" ");
-
-    if (parts.length == 3) {
-      try {
-        result = ODataHttpMethod.valueOf(parts[0]);
-      } catch (IllegalArgumentException e) {
-        throw new BatchException(BatchException.MISSING_METHOD.addContent(httpRequest.getLineNumber()), e);
-      }
-    } else {
-      throw new BatchException(BatchException.INVALID_REQUEST_LINE.addContent(httpRequest.toString()).addContent(
-          httpRequest.getLineNumber()));
-    }
-
-    return result;
-  }
-
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/86c4ddc4/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchResponseTransformator.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchResponseTransformator.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchResponseTransformator.java
index e800673..5e4198c 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchResponseTransformator.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchResponseTransformator.java
@@ -20,8 +20,6 @@ package org.apache.olingo.odata2.core.batch.v2;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 import org.apache.olingo.odata2.api.batch.BatchException;
 import org.apache.olingo.odata2.api.batch.BatchParserResult;
@@ -29,12 +27,10 @@ import org.apache.olingo.odata2.api.client.batch.BatchSingleResponse;
 import org.apache.olingo.odata2.api.uri.PathInfo;
 import org.apache.olingo.odata2.core.batch.BatchHelper;
 import org.apache.olingo.odata2.core.batch.BatchSingleResponseImpl;
-import org.apache.olingo.odata2.core.batch.v2.BufferedReaderIncludingLineEndings.Line;
+import org.apache.olingo.odata2.core.batch.v2.BatchTransformatorCommon.HttpResponsetStatusLine;
 
 public class BatchResponseTransformator implements BatchTransformator {
 
-  private static final String REG_EX_STATUS_LINE = "(?:HTTP/[0-9]\\.[0-9])\\s([0-9]{3})\\s([\\S ]+)\\s*";
-
   public BatchResponseTransformator() {}
 
   @Override
@@ -81,46 +77,26 @@ public class BatchResponseTransformator implements BatchTransformator {
   private BatchSingleResponse transformQueryOperation(final BatchQueryOperation operation, final String contentId)
       throws BatchException {
 
-    final Matcher statusMatcher = prepareStatusLineMatcher(operation.getHttpStatusLine());
+    final HttpResponsetStatusLine statusLine = new HttpResponsetStatusLine(operation.getHttpStatusLine());
 
     BatchSingleResponseImpl response = new BatchSingleResponseImpl();
     response.setContentId(contentId);
     response.setHeaders(operation.getHeaders().toSingleMap());
-    response.setStatusCode(getStatusCode(statusMatcher));
-    response.setStatusInfo(getStatusInfo(statusMatcher));
+    response.setStatusCode(statusLine.getStatusCode());
+    response.setStatusInfo(statusLine.getStatusInfo());
     response.setBody(getBody(operation));
 
     return response;
   }
 
-  private Matcher prepareStatusLineMatcher(final Line httpStatusLine) throws BatchException {
-    final Pattern regexPattern = Pattern.compile(REG_EX_STATUS_LINE);
-    final Matcher matcher = regexPattern.matcher(httpStatusLine.toString());
-
-    if (matcher.find()) {
-      return matcher;
-    } else {
-      throw new BatchException(BatchException.INVALID_STATUS_LINE.addContent(httpStatusLine.toString())
-          .addContent(httpStatusLine.getLineNumber()));
-    }
-  }
-
   private String getBody(final BatchQueryOperation operation) throws BatchException {
     int contentLength = BatchTransformatorCommon.getContentLength(operation.getHeaders());
 
     if (contentLength == -1) {
-      return BatchParserCommon.stringListToString(operation.getBody());
+      return BatchParserCommon.lineListToString(operation.getBody());
     } else {
       return BatchParserCommon.trimLineListToLength(operation.getBody(), contentLength);
     }
   }
 
-  private String getStatusCode(final Matcher matcher) throws BatchException {
-    return matcher.group(1);
-  }
-
-  private String getStatusInfo(final Matcher matcher) throws BatchException {
-    return matcher.group(2);
-  }
-
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/86c4ddc4/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchTransformatorCommon.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchTransformatorCommon.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchTransformatorCommon.java
index 521b4e1..c07c06f 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchTransformatorCommon.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchTransformatorCommon.java
@@ -18,16 +18,29 @@
  ******************************************************************************/
 package org.apache.olingo.odata2.core.batch.v2;
 
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import org.apache.olingo.odata2.api.batch.BatchException;
 import org.apache.olingo.odata2.api.commons.HttpContentType;
 import org.apache.olingo.odata2.api.commons.HttpHeaders;
+import org.apache.olingo.odata2.api.commons.ODataHttpMethod;
+import org.apache.olingo.odata2.api.uri.PathInfo;
+import org.apache.olingo.odata2.api.uri.PathSegment;
+import org.apache.olingo.odata2.core.ODataPathSegmentImpl;
+import org.apache.olingo.odata2.core.PathInfoImpl;
 import org.apache.olingo.odata2.core.batch.BatchHelper;
+import org.apache.olingo.odata2.core.batch.v2.BufferedReaderIncludingLineEndings.Line;
 import org.apache.olingo.odata2.core.batch.v2.Header.HeaderField;
 
 public class BatchTransformatorCommon {
-
   public static void validateContentType(final Header headers) throws BatchException {
     List<String> contentTypes = headers.getHeaders(HttpHeaders.CONTENT_TYPE);
 
@@ -87,4 +100,177 @@ public class BatchTransformatorCommon {
 
     return -1;
   }
+  
+  public static class HttpResponsetStatusLine {
+    private static final String REG_EX_STATUS_LINE = "(?:HTTP/[0-9]\\.[0-9])\\s([0-9]{3})\\s([\\S ]+)\\s*";
+    private Line httpStatusLine;
+    private String statusCode;
+    private String statusInfo;
+
+    public HttpResponsetStatusLine(final Line httpStatusLine) throws BatchException {
+      this.httpStatusLine = httpStatusLine;
+      parse();
+    }
+
+    private void parse() throws BatchException {
+      final Pattern regexPattern = Pattern.compile(REG_EX_STATUS_LINE);
+      final Matcher matcher = regexPattern.matcher(httpStatusLine.toString());
+
+      if (matcher.find()) {
+        statusCode = matcher.group(1);
+        statusInfo = matcher.group(2);
+      } else {
+        throw new BatchException(BatchException.INVALID_STATUS_LINE.addContent(httpStatusLine.toString())
+            .addContent(httpStatusLine.getLineNumber()));
+      }
+    }
+
+    public String getStatusCode() {
+      return statusCode;
+    }
+
+    public String getStatusInfo() {
+      return statusInfo;
+    }
+  }
+
+  public static class HttpRequestStatusLine {
+    private static final Set<String> HTTP_BATCH_METHODS = new HashSet<String>(Arrays.asList(new String[] { "GET" }));
+    private static final Set<String> HTTP_CHANGE_SET_METHODS = new HashSet<String>(Arrays.asList(new String[] { "POST",
+        "PUT", "DELETE", "MERGE", "PATCH" }));
+    private static final String HTTP_VERSION = "HTTP/1.1";
+    
+    final private Line statusLine;
+    final String requestBaseUri;
+    final PathInfo batchRequestPathInfo;
+
+    private ODataHttpMethod method;
+    private PathInfo pathInfo;
+    private String httpVersion;
+
+    public HttpRequestStatusLine(final Line httpStatusLine, final String baseUri, final PathInfo pathInfo)
+        throws BatchException {
+      statusLine = httpStatusLine;
+      requestBaseUri = baseUri;
+      batchRequestPathInfo = pathInfo;
+
+      parse();
+    }
+
+    private void parse() throws BatchException {
+      final String[] parts = statusLine.toString().split(" ");
+
+      if (parts.length == 3) {
+        try {
+          method = parseMethod(parts[0]);
+          pathInfo = parseUri(parts[1]);
+          httpVersion = parseHttpVersion(parts[2]);
+        } catch (IllegalArgumentException e) {
+          throw new BatchException(BatchException.MISSING_METHOD.addContent(statusLine.getLineNumber()), e);
+        }
+      } else {
+        throw new BatchException(BatchException.INVALID_REQUEST_LINE.addContent(statusLine.toString())
+            .addContent(statusLine.getLineNumber()));
+      }
+    }
+
+    private ODataHttpMethod parseMethod(final String method) throws BatchException {
+      try {
+        return ODataHttpMethod.valueOf(method.trim());
+      } catch (IllegalArgumentException e) {
+        throw new BatchException(BatchException.MISSING_METHOD.addContent(statusLine.getLineNumber()), e);
+      }
+    }
+
+    private PathInfo parseUri(final String uri)
+        throws BatchException {
+      final PathInfoImpl pathInfo = new PathInfoImpl();
+      final String odataPathSegmentsAsString;
+      final String queryParametersAsString;
+      Pattern regexRequestUri;
+
+      pathInfo.setServiceRoot(batchRequestPathInfo.getServiceRoot());
+      pathInfo.setPrecedingPathSegment(batchRequestPathInfo.getPrecedingSegments());
+
+      try {
+        URI uriObject = new URI(uri);
+        if (uriObject.isAbsolute()) {
+          regexRequestUri = Pattern.compile(requestBaseUri + "/([^/][^?]*)(\\?.*)?");
+        } else {
+          regexRequestUri = BatchParserCommon.PATTERN_RELATIVE_URI;
+
+        }
+
+        Matcher uriParts = regexRequestUri.matcher(uri);
+
+        if (uriParts.lookingAt() && uriParts.groupCount() == 2) {
+          odataPathSegmentsAsString = uriParts.group(1);
+          queryParametersAsString = uriParts.group(2) != null ? uriParts.group(2) : "";
+
+          pathInfo.setODataPathSegment(parseODataPathSegments(odataPathSegmentsAsString));
+          if (!odataPathSegmentsAsString.startsWith("$")) {
+            String requestUri = requestBaseUri + "/" + odataPathSegmentsAsString + queryParametersAsString;
+            pathInfo.setRequestUri(new URI(requestUri));
+          }
+
+        } else {
+          throw new BatchException(BatchException.INVALID_URI.addContent(statusLine.getLineNumber()));
+        }
+      } catch (URISyntaxException e) {
+        throw new BatchException(BatchException.INVALID_URI.addContent(statusLine.getLineNumber()), e);
+      }
+
+      return pathInfo;
+    }
+
+    private List<PathSegment> parseODataPathSegments(final String odataPathSegmentsAsString) {
+      final List<PathSegment> odataPathSegments = new ArrayList<PathSegment>();
+      final String[] pathParts = odataPathSegmentsAsString.split("/");
+
+      for (final String pathSegment : pathParts) {
+        odataPathSegments.add(new ODataPathSegmentImpl(pathSegment, null));
+      }
+
+      return odataPathSegments;
+    }
+
+    private String parseHttpVersion(final String httpVersion) throws BatchException {
+      if (!HTTP_VERSION.equals(httpVersion.trim())) {
+        throw new BatchException(BatchException.INVALID_REQUEST_LINE
+                                                  .addContent(statusLine.toString())
+                                                  .addContent(statusLine.getLineNumber()));
+      } else {
+        return HTTP_VERSION;
+      }
+    }
+
+    public void validateHttpMethod(boolean isChangeSet) throws BatchException {
+      Set<String> validMethods = (isChangeSet) ? HTTP_CHANGE_SET_METHODS : HTTP_BATCH_METHODS;
+      
+      if (!validMethods.contains(getMethod().toString())) {
+        if (isChangeSet) {
+          throw new BatchException(BatchException.INVALID_CHANGESET_METHOD.addContent(statusLine.getLineNumber()));
+        } else {
+          throw new BatchException(BatchException.INVALID_QUERY_OPERATION_METHOD
+              .addContent(statusLine.getLineNumber()));
+        }
+      }
+    }
+
+    public ODataHttpMethod getMethod() {
+      return method;
+    }
+
+    public PathInfo getPathInfo() {
+      return pathInfo;
+    }
+
+    public String getHttpVersion() {
+      return httpVersion;
+    }
+
+    public int getLineNumber() {
+      return statusLine.getLineNumber();
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/86c4ddc4/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestParserTest.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestParserTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestParserTest.java
index 52550fb..1a374a5 100644
--- a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestParserTest.java
+++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestParserTest.java
@@ -41,7 +41,6 @@ import org.apache.olingo.odata2.core.PathInfoImpl;
 import org.apache.olingo.odata2.core.batch.v2.BatchParser;
 import org.apache.olingo.odata2.testutil.helper.StringHelper;
 import org.junit.BeforeClass;
-import org.junit.Ignore;
 import org.junit.Test;
 
 public class BatchRequestParserTest {
@@ -119,7 +118,7 @@ public class BatchRequestParserTest {
       }
     }
   }
-
+  
   @Test
   public void testImageInContent() throws IOException, BatchException, URISyntaxException {
     String fileName = "/batchWithContent.batch";
@@ -271,6 +270,51 @@ public class BatchRequestParserTest {
     parseInvalidBatchBody(batch);
   }
 
+  @Test(expected=BatchException.class)
+  public void testMissingHttpVersion() throws BatchException {
+    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
+        + "--batch_8194-cf13-1f56--";
+    
+    parseInvalidBatchBody(batch);
+  }
+  
+  @Test(expected=BatchException.class)
+  public void testMissingHttpVersion2() throws BatchException {
+    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
+        + "--batch_8194-cf13-1f56--";
+    
+    parseInvalidBatchBody(batch);
+  }
+  
+  @Test(expected=BatchException.class)
+  public void testMissingHttpVersion3() throws BatchException {
+    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
+        + "--batch_8194-cf13-1f56--";
+    
+    parseInvalidBatchBody(batch);
+  }
+  
   @Test(expected = BatchException.class)
   public void testBoundaryWithoutHyphen() throws BatchException {
     String batch = "--batch_8194-cf13-1f56" + CRLF
@@ -347,19 +391,6 @@ public class BatchRequestParserTest {
   }
 
   @Test(expected = BatchException.class)
-  @Ignore("What should here throw an exception")
-  public void testMimeHeaderContentId() throws BatchException {
-    String batch = "--batch_8194-cf13-1f56" + CRLF
-        + MIME_HEADERS
-        + "Content-ID: 1" + CRLF
-        + CRLF
-        + "GET Employees('1')/EmployeeName HTTP/1.1" + CRLF
-        + CRLF
-        + "--batch_8194-cf13-1f56--";
-    parseInvalidBatchBody(batch);
-  }
-
-  @Test(expected = BatchException.class)
   public void testInvalidMethodForBatch() throws BatchException {
     String batch = "--batch_8194-cf13-1f56" + CRLF
         + MIME_HEADERS
@@ -413,7 +444,7 @@ public class BatchRequestParserTest {
     parseInvalidBatchBody(batch);
   }
 
-  @Test(expected = BatchException.class)
+  @Test(expected=BatchException.class)
   public void testInvalidChangeSetBoundary() throws BatchException {
     String batch = "--batch_8194-cf13-1f56" + CRLF
         + "Content-Type: multipart/mixed;boundary=changeset_f980-1cb6-94dd" + CRLF
@@ -425,10 +456,37 @@ public class BatchRequestParserTest {
         + "Content-Type: application/json;odata=verbose" + CRLF
         + "MaxDataServiceVersion: 2.0" + CRLF
         + CRLF
+        + "--changeset_f980-1cb6-94dd--" + CRLF
+        + CRLF
         + "--batch_8194-cf13-1f56--";
     parseInvalidBatchBody(batch);
   }
-
+  
+  @Test(expected=BatchException.class)
+  public void testNestedChangeset() throws BatchException {
+    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
+        + CRLF
+        + "Content-Type: multipart/mixed;boundary=changeset_f980-1cb6-94dd2" + CRLF
+        + CRLF
+        + "--changeset_f980-1cb6-94dd2" + 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
+        + "--changeset_f980-1cb6-94dd--" + CRLF
+        + CRLF
+        + "--batch_8194-cf13-1f56--";
+    parse(batch);
+  }
+  
   @Test(expected = BatchException.class)
   public void testMissingContentTransferEncoding() throws BatchException {
     String batch = "--batch_8194-cf13-1f56" + CRLF

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/86c4ddc4/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestTest.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestTest.java
index bcf13e4..116bfd7 100644
--- a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestTest.java
+++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestTest.java
@@ -132,7 +132,6 @@ public class BatchRequestTest {
   }
 
   @Test
-  // TODO
       /*
        * --batch_123
        * Content-Type: application/http

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/86c4ddc4/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BufferedReaderIncludingLineEndingsTest.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BufferedReaderIncludingLineEndingsTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BufferedReaderIncludingLineEndingsTest.java
index 1161530..69b546f 100644
--- a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BufferedReaderIncludingLineEndingsTest.java
+++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BufferedReaderIncludingLineEndingsTest.java
@@ -378,7 +378,18 @@ public class BufferedReaderIncludingLineEndingsTest {
     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");