mirror of
https://github.com/apache/olingo-odata4.git
synced 2025-03-06 08:39:14 +00:00
[OLINGO-472] BatchParser first draft
Signed-off-by: Christian Amend <chrisam@apache.org>
This commit is contained in:
parent
3e11738125
commit
32247295fc
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.olingo.server.api.batch;
|
||||
|
||||
public interface BatchParserResult {
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.olingo.server.api.batch;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.olingo.server.api.ODataRequest;
|
||||
|
||||
/**
|
||||
* A BatchPart
|
||||
* <p> BatchPart represents a distinct MIME part of a Batch Request body. It can be ChangeSet or Query Operation
|
||||
*/
|
||||
public interface BatchRequestPart extends BatchParserResult {
|
||||
|
||||
/**
|
||||
* Get the info if a BatchPart is a ChangeSet
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isChangeSet();
|
||||
|
||||
/**
|
||||
* Get requests. If a BatchPart is a Query Operation, the list contains one request.
|
||||
* @return a list of {@link ODataRequest}
|
||||
*/
|
||||
public List<ODataRequest> getRequests();
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.olingo.server.api.batch;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.olingo.server.api.ODataResponse;
|
||||
|
||||
public interface ODataResponsePart {
|
||||
/**
|
||||
* Returns a collection of ODataResponses.
|
||||
* Each collections contains at least one {@link ODataResponse}.
|
||||
*
|
||||
* If this instance represents a change set, there are may many ODataResponses
|
||||
*
|
||||
* @return a list of {@link ODataResponse}
|
||||
*/
|
||||
public List<ODataResponse> getResponses();
|
||||
|
||||
/**
|
||||
* Returns true if the current instance represents a change set.
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
public boolean isChangeSet();
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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 org.apache.olingo.server.api.ODataTranslatedException;
|
||||
|
||||
public class BatchException extends ODataTranslatedException {
|
||||
public static enum MessageKeys implements MessageKey {
|
||||
INVALID_BOUNDARY,
|
||||
INVALID_CHANGESET_METHOD,
|
||||
INVALID_CONTENT,
|
||||
INVALID_CONTENT_LENGTH,
|
||||
INVALID_CONTENT_TRANSFER_ENCODING,
|
||||
INVALID_CONTENT_TYPE,
|
||||
INVALID_HEADER,
|
||||
INVALID_HTTP_VERSION,
|
||||
INVALID_METHOD,
|
||||
INVALID_QUERY_OPERATION_METHOD,
|
||||
INVALID_STATUS_LINE,
|
||||
INVALID_URI,
|
||||
MISSING_BLANK_LINE,
|
||||
MISSING_BOUNDARY_DELIMITER,
|
||||
MISSING_CLOSE_DELIMITER,
|
||||
MISSING_CONTENT_ID,
|
||||
MISSING_CONTENT_TRANSFER_ENCODING,
|
||||
MISSING_CONTENT_TYPE,
|
||||
MISSING_MANDATORY_HEADER, FORBIDDEN_HEADER;
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return name();
|
||||
}
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = -907752788975531134L;
|
||||
|
||||
public BatchException(final String developmentMessage, final MessageKey messageKey, final int lineNumber) {
|
||||
this(developmentMessage, messageKey, "" + lineNumber);
|
||||
}
|
||||
|
||||
public BatchException(final String developmentMessage, final MessageKey messageKey, final String... parameters) {
|
||||
super(developmentMessage, messageKey, parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBundleName() {
|
||||
return DEFAULT_SERVER_BUNDLE_NAME;
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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 java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import org.apache.olingo.commons.api.ODataRuntimeException;
|
||||
import org.apache.olingo.server.core.batch.parser.BufferedReaderIncludingLineEndings;
|
||||
|
||||
public class StringUtil {
|
||||
|
||||
|
||||
public static String toString(final InputStream in) throws IOException {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
final BufferedReaderIncludingLineEndings reader = new BufferedReaderIncludingLineEndings(new InputStreamReader(in));
|
||||
String currentLine;
|
||||
|
||||
while((currentLine = reader.readLine()) != null) {
|
||||
builder.append(currentLine);
|
||||
}
|
||||
|
||||
reader.close();
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static InputStream toInputStream(final String string) {
|
||||
try {
|
||||
return new ByteArrayInputStream(string.getBytes("UTF-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new ODataRuntimeException("Charset UTF-8 not found");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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 java.util.LinkedList;
|
||||
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.BufferedReaderIncludingLineEndings.Line;
|
||||
|
||||
public class BatchBodyPart implements BatchPart {
|
||||
final private String boundary;
|
||||
final private boolean isStrict;
|
||||
final List<Line> remainingMessage = new LinkedList<Line>();
|
||||
|
||||
private Header headers;
|
||||
private boolean isChangeSet;
|
||||
private List<BatchQueryOperation> requests;
|
||||
|
||||
public BatchBodyPart(final List<Line> message, final String boundary, final boolean isStrict) {
|
||||
this.boundary = boundary;
|
||||
this.isStrict = isStrict;
|
||||
remainingMessage.addAll(message);
|
||||
}
|
||||
|
||||
public BatchBodyPart parse() throws BatchException {
|
||||
headers = BatchParserCommon.consumeHeaders(remainingMessage);
|
||||
BatchParserCommon.consumeBlankLine(remainingMessage, isStrict);
|
||||
isChangeSet = isChangeSet(headers);
|
||||
requests = consumeRequest(remainingMessage);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private boolean isChangeSet(final Header header) throws BatchException {
|
||||
final List<String> contentTypes = headers.getHeaders(HttpHeader.CONTENT_TYPE);
|
||||
boolean isChangeSet = false;
|
||||
|
||||
if (contentTypes.size() == 0) {
|
||||
throw new BatchException("Missing content type", BatchException.MessageKeys.MISSING_CONTENT_TYPE, ""
|
||||
+ headers.getLineNumber());
|
||||
}
|
||||
|
||||
for (String contentType : contentTypes) {
|
||||
if (isContentTypeMultiPartMixed(contentType)) {
|
||||
isChangeSet = true;
|
||||
}
|
||||
}
|
||||
|
||||
return isChangeSet;
|
||||
}
|
||||
|
||||
private List<BatchQueryOperation> consumeRequest(final List<Line> remainingMessage) throws BatchException {
|
||||
if (isChangeSet) {
|
||||
return consumeChangeSet(remainingMessage);
|
||||
} else {
|
||||
return consumeQueryOperation(remainingMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private List<BatchQueryOperation> consumeChangeSet(final List<Line> remainingMessage2) throws BatchException {
|
||||
final List<List<Line>> changeRequests = splitChangeSet(remainingMessage);
|
||||
final List<BatchQueryOperation> requestList = new LinkedList<BatchQueryOperation>();
|
||||
|
||||
for (List<Line> changeRequest : changeRequests) {
|
||||
requestList.add(new BatchChangeSetPart(changeRequest, isStrict).parse());
|
||||
}
|
||||
|
||||
return requestList;
|
||||
}
|
||||
|
||||
private List<List<Line>> splitChangeSet(final List<Line> remainingMessage2) throws BatchException {
|
||||
|
||||
final HeaderField contentTypeField = headers.getHeaderField(HttpHeader.CONTENT_TYPE);
|
||||
final String changeSetBoundary = BatchParserCommon.getBoundary(contentTypeField.getValueNotNull(),
|
||||
contentTypeField.getLineNumber());
|
||||
validateChangeSetBoundary(changeSetBoundary, headers);
|
||||
|
||||
return BatchParserCommon.splitMessageByBoundary(remainingMessage, changeSetBoundary);
|
||||
}
|
||||
|
||||
private void validateChangeSetBoundary(final String changeSetBoundary, final Header header) throws BatchException {
|
||||
if (changeSetBoundary.equals(boundary)) {
|
||||
throw new BatchException("Change set boundary is equals to batch request boundary",
|
||||
BatchException.MessageKeys.INVALID_BOUNDARY,
|
||||
"" + header.getHeaderField(HttpHeader.CONTENT_TYPE).getLineNumber());
|
||||
}
|
||||
}
|
||||
|
||||
private List<BatchQueryOperation> consumeQueryOperation(final List<Line> remainingMessage) throws BatchException {
|
||||
final List<BatchQueryOperation> requestList = new LinkedList<BatchQueryOperation>();
|
||||
requestList.add(new BatchQueryOperation(remainingMessage, isStrict).parse());
|
||||
|
||||
return requestList;
|
||||
}
|
||||
|
||||
private boolean isContentTypeMultiPartMixed(final String contentType) {
|
||||
return BatchParserCommon.PATTERN_MULTIPART_BOUNDARY.matcher(contentType).matches();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Header getHeaders() {
|
||||
return headers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStrict() {
|
||||
return isStrict;
|
||||
}
|
||||
|
||||
public boolean isChangeSet() {
|
||||
return isChangeSet;
|
||||
}
|
||||
|
||||
public List<BatchQueryOperation> getRequests() {
|
||||
return requests;
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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 java.util.List;
|
||||
|
||||
import org.apache.olingo.server.core.batch.BatchException;
|
||||
import org.apache.olingo.server.core.batch.parser.BufferedReaderIncludingLineEndings.Line;
|
||||
|
||||
public class BatchChangeSetPart extends BatchQueryOperation {
|
||||
private BatchQueryOperation request;
|
||||
|
||||
public BatchChangeSetPart(final List<Line> message, final boolean isStrict) throws BatchException {
|
||||
super(message, isStrict);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BatchChangeSetPart parse() throws BatchException {
|
||||
headers = BatchParserCommon.consumeHeaders(message);
|
||||
BatchParserCommon.consumeBlankLine(message, isStrict);
|
||||
|
||||
request = new BatchQueryOperation(message, isStrict).parse();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public BatchQueryOperation getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Line> getBody() {
|
||||
return request.getBody();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Line getHttpStatusLine() {
|
||||
return request.getHttpStatusLine();
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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 java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.olingo.commons.api.ODataRuntimeException;
|
||||
import org.apache.olingo.server.api.batch.BatchParserResult;
|
||||
import org.apache.olingo.server.api.batch.BatchRequestPart;
|
||||
import org.apache.olingo.server.core.batch.BatchException;
|
||||
import org.apache.olingo.server.core.batch.parser.BufferedReaderIncludingLineEndings.Line;
|
||||
import org.apache.olingo.server.core.batch.transformator.BatchRequestTransformator;
|
||||
|
||||
public class BatchParser {
|
||||
|
||||
private final String contentTypeMime;
|
||||
private final String baseUri;
|
||||
private final String rawServiceResolutionUri;
|
||||
private final boolean isStrict;
|
||||
|
||||
public BatchParser(final String contentType, final String baseUri, final String serviceResolutionUri,
|
||||
final boolean isStrict) {
|
||||
contentTypeMime = contentType;
|
||||
this.baseUri = BatchParserCommon.removeEndingSlash(baseUri);
|
||||
this.isStrict = isStrict;
|
||||
this.rawServiceResolutionUri = serviceResolutionUri;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<BatchRequestPart> parseBatchRequest(final InputStream in) throws BatchException {
|
||||
return (List<BatchRequestPart>) parse(in, new BatchRequestTransformator(baseUri, rawServiceResolutionUri));
|
||||
}
|
||||
|
||||
private List<? extends BatchParserResult> parse(final InputStream in, final BatchRequestTransformator transformator)
|
||||
throws BatchException {
|
||||
try {
|
||||
return parseBatch(in, transformator);
|
||||
} catch (IOException e) {
|
||||
throw new ODataRuntimeException(e);
|
||||
} finally {
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException e) {
|
||||
throw new ODataRuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<BatchParserResult> parseBatch(final InputStream in, final BatchRequestTransformator transformator)
|
||||
throws IOException, BatchException {
|
||||
final String boundary = BatchParserCommon.getBoundary(contentTypeMime, 1);
|
||||
final List<BatchParserResult> resultList = new LinkedList<BatchParserResult>();
|
||||
final List<List<Line>> bodyPartStrings = splitBodyParts(in, boundary);
|
||||
|
||||
for (List<Line> bodyPartString : bodyPartStrings) {
|
||||
BatchBodyPart bodyPart = new BatchBodyPart(bodyPartString, boundary, isStrict).parse();
|
||||
resultList.addAll(transformator.transform(bodyPart));
|
||||
}
|
||||
|
||||
return resultList;
|
||||
}
|
||||
|
||||
private List<List<Line>> splitBodyParts(final InputStream in, final String boundary) throws IOException,
|
||||
BatchException {
|
||||
final BufferedReaderIncludingLineEndings reader = new BufferedReaderIncludingLineEndings(new InputStreamReader(in));
|
||||
final List<Line> message = reader.toLineList();
|
||||
reader.close();
|
||||
|
||||
return BatchParserCommon.splitMessageByBoundary(message, boundary);
|
||||
}
|
||||
}
|
@ -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.parser;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.olingo.commons.api.http.HttpContentType;
|
||||
import org.apache.olingo.server.core.batch.BatchException;
|
||||
import org.apache.olingo.server.core.batch.parser.BufferedReaderIncludingLineEndings.Line;
|
||||
|
||||
public class BatchParserCommon {
|
||||
|
||||
private static final String REG_EX_BOUNDARY =
|
||||
"([a-zA-Z0-9_\\-\\.'\\+]{1,70})|\"([a-zA-Z0-9_\\-\\.'\\+\\s\\" +
|
||||
"(\\),/:=\\?]{1,69}[a-zA-Z0-9_\\-\\.'\\+\\(\\),/:=\\?])\"";
|
||||
private static final Pattern PATTERN_LAST_CRLF = Pattern.compile("(.*)(\r\n){1}( *)", Pattern.DOTALL);
|
||||
private static final Pattern PATTERN_HEADER_LINE = Pattern.compile("([a-zA-Z\\-]+):\\s?(.*)\\s*");
|
||||
private static final String REG_EX_APPLICATION_HTTP = "application/http";
|
||||
|
||||
public static final Pattern PATTERN_MULTIPART_BOUNDARY = Pattern.compile("multipart/mixed(.*)",
|
||||
Pattern.CASE_INSENSITIVE);
|
||||
public static final Pattern PATTERN_CONTENT_TYPE_APPLICATION_HTTP = Pattern.compile(REG_EX_APPLICATION_HTTP,
|
||||
Pattern.CASE_INSENSITIVE);
|
||||
public static final String BINARY_ENCODING = "binary";
|
||||
public static final String HTTP_CONTENT_ID = "Content-Id";
|
||||
public static final String HTTP_CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding";
|
||||
|
||||
public static final String HTTP_EXPECT = "Expect";
|
||||
public static final String HTTP_FROM = "From";
|
||||
public static final String HTTP_MAX_FORWARDS = "Max-Forwards";
|
||||
public static final String HTTP_RANGE = "Range";
|
||||
public static final String HTTP_TE = "TE";
|
||||
|
||||
public static String getBoundary(final String contentType, final int line) throws BatchException {
|
||||
if (contentType.toLowerCase(Locale.ENGLISH).startsWith("multipart/mixed")) {
|
||||
final String[] parameter = contentType.split(";");
|
||||
|
||||
for (final String pair : parameter) {
|
||||
|
||||
final String[] attrValue = pair.split("=");
|
||||
if (attrValue.length == 2 && "boundary".equals(attrValue[0].trim().toLowerCase(Locale.ENGLISH))) {
|
||||
if (attrValue[1].matches(REG_EX_BOUNDARY)) {
|
||||
return trimQuota(attrValue[1].trim());
|
||||
} else {
|
||||
throw new BatchException("Invalid boundary format", BatchException.MessageKeys.INVALID_BOUNDARY, "" + line);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
throw new BatchException("Content type is not multipart mixed",
|
||||
BatchException.MessageKeys.INVALID_CONTENT_TYPE, HttpContentType.MULTIPART_MIXED);
|
||||
}
|
||||
|
||||
public static String removeEndingSlash(String content) {
|
||||
content = content.trim();
|
||||
int lastSlashIndex = content.lastIndexOf('/');
|
||||
|
||||
return (lastSlashIndex == content.length() - 1) ? content.substring(0, content.length() - 1) : content;
|
||||
}
|
||||
|
||||
private static String trimQuota(String boundary) {
|
||||
if (boundary.matches("\".*\"")) {
|
||||
boundary = boundary.replace("\"", "");
|
||||
}
|
||||
|
||||
return boundary;
|
||||
}
|
||||
|
||||
public static List<List<Line>> splitMessageByBoundary(final List<Line> message, final String boundary)
|
||||
throws BatchException {
|
||||
final List<List<Line>> messageParts = new LinkedList<List<Line>>();
|
||||
List<Line> currentPart = new ArrayList<Line>();
|
||||
boolean isEndReached = false;
|
||||
|
||||
final String quotedBoundary = Pattern.quote(boundary);
|
||||
final Pattern boundaryDelimiterPattern = Pattern.compile("--" + quotedBoundary + "--[\\s ]*");
|
||||
final Pattern boundaryPattern = Pattern.compile("--" + quotedBoundary + "[\\s ]*");
|
||||
|
||||
for (Line currentLine : message) {
|
||||
if (boundaryDelimiterPattern.matcher(currentLine.toString()).matches()) {
|
||||
removeEndingCRLFFromList(currentPart);
|
||||
messageParts.add(currentPart);
|
||||
isEndReached = true;
|
||||
} else if (boundaryPattern.matcher(currentLine.toString()).matches()) {
|
||||
removeEndingCRLFFromList(currentPart);
|
||||
messageParts.add(currentPart);
|
||||
currentPart = new LinkedList<Line>();
|
||||
} else {
|
||||
currentPart.add(currentLine);
|
||||
}
|
||||
|
||||
if (isEndReached) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
final int lineNumer = (message.size() > 0) ? message.get(0).getLineNumber() : 0;
|
||||
// Remove preamble
|
||||
if (messageParts.size() > 0) {
|
||||
messageParts.remove(0);
|
||||
} else {
|
||||
throw new BatchException("Missing boundary delimiter", BatchException.MessageKeys.MISSING_BOUNDARY_DELIMITER, ""
|
||||
+ lineNumer);
|
||||
}
|
||||
|
||||
if (!isEndReached) {
|
||||
throw new BatchException("Missing close boundary delimiter", BatchException.MessageKeys.MISSING_CLOSE_DELIMITER,
|
||||
"" + lineNumer);
|
||||
}
|
||||
|
||||
if (messageParts.size() == 0) {
|
||||
throw new BatchException("No boundary delimiter found",
|
||||
BatchException.MessageKeys.MISSING_BOUNDARY_DELIMITER, boundary, "" + lineNumer);
|
||||
}
|
||||
|
||||
return messageParts;
|
||||
}
|
||||
|
||||
private static void removeEndingCRLFFromList(final List<Line> list) {
|
||||
if (list.size() > 0) {
|
||||
Line lastLine = list.remove(list.size() - 1);
|
||||
list.add(removeEndingCRLF(lastLine));
|
||||
}
|
||||
}
|
||||
|
||||
public static Line removeEndingCRLF(final Line line) {
|
||||
Pattern pattern = PATTERN_LAST_CRLF;
|
||||
Matcher matcher = pattern.matcher(line.toString());
|
||||
|
||||
if (matcher.matches()) {
|
||||
return new Line(matcher.group(1), line.getLineNumber());
|
||||
} else {
|
||||
return line;
|
||||
}
|
||||
}
|
||||
|
||||
public static Header consumeHeaders(final List<Line> remainingMessage) {
|
||||
final int headerLineNumber = remainingMessage.size() != 0 ? remainingMessage.get(0).getLineNumber() : 0;
|
||||
final Header headers = new Header(headerLineNumber);
|
||||
final Iterator<Line> iter = remainingMessage.iterator();
|
||||
Line currentLine;
|
||||
boolean isHeader = true;
|
||||
|
||||
while (iter.hasNext() && isHeader) {
|
||||
currentLine = iter.next();
|
||||
final Matcher headerMatcher = PATTERN_HEADER_LINE.matcher(currentLine.toString());
|
||||
|
||||
if (headerMatcher.matches() && headerMatcher.groupCount() == 2) {
|
||||
iter.remove();
|
||||
|
||||
String headerName = headerMatcher.group(1).trim();
|
||||
String headerValue = headerMatcher.group(2).trim();
|
||||
|
||||
headers.addHeader(headerName, Header.splitValuesByComma(headerValue), currentLine.getLineNumber());
|
||||
} else {
|
||||
isHeader = false;
|
||||
}
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
public static void consumeBlankLine(final List<Line> remainingMessage, final boolean isStrict) throws BatchException {
|
||||
if (remainingMessage.size() > 0 && remainingMessage.get(0).toString().matches("\\s*\r\n\\s*")) {
|
||||
remainingMessage.remove(0);
|
||||
} else {
|
||||
if (isStrict) {
|
||||
final int lineNumber = (remainingMessage.size() > 0) ? remainingMessage.get(0).getLineNumber() : 0;
|
||||
throw new BatchException("Missing blank line", BatchException.MessageKeys.MISSING_BLANK_LINE, "[None]", ""
|
||||
+ lineNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static InputStream convertLineListToInputStream(List<Line> messageList) {
|
||||
final String message = lineListToString(messageList);
|
||||
|
||||
return new ByteArrayInputStream(message.getBytes());
|
||||
}
|
||||
|
||||
private static String lineListToString(List<Line> messageList) {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
|
||||
for (Line currentLine : messageList) {
|
||||
builder.append(currentLine.toString());
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static String trimLineListToLength(final List<Line> list, final int length) {
|
||||
final String message = lineListToString(list);
|
||||
final int lastIndex = Math.min(length, message.length());
|
||||
|
||||
return (lastIndex > 0) ? message.substring(0, lastIndex) : "";
|
||||
}
|
||||
|
||||
public static InputStream convertLineListToInputStream(List<Line> list, int length) {
|
||||
final String message = trimLineListToLength(list, length);
|
||||
|
||||
return new ByteArrayInputStream(message.getBytes());
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
public interface BatchPart {
|
||||
public Header getHeaders();
|
||||
|
||||
public boolean isStrict();
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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 java.util.List;
|
||||
|
||||
import org.apache.olingo.server.core.batch.BatchException;
|
||||
import org.apache.olingo.server.core.batch.parser.BufferedReaderIncludingLineEndings.Line;
|
||||
|
||||
public class BatchQueryOperation implements BatchPart {
|
||||
|
||||
protected final boolean isStrict;
|
||||
protected Line httpStatusLine;
|
||||
protected Header headers;
|
||||
protected List<Line> body;
|
||||
protected int bodySize;
|
||||
protected List<Line> message;
|
||||
|
||||
public BatchQueryOperation(final List<Line> message, final boolean isStrict) {
|
||||
this.isStrict = isStrict;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public BatchQueryOperation parse() throws BatchException {
|
||||
httpStatusLine = consumeHttpStatusLine(message);
|
||||
headers = BatchParserCommon.consumeHeaders(message);
|
||||
BatchParserCommon.consumeBlankLine(message, isStrict);
|
||||
body = message;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
protected Line consumeHttpStatusLine(final List<Line> message) throws BatchException {
|
||||
if (message.size() > 0 && !message.get(0).toString().trim().equals("")) {
|
||||
final Line method = message.get(0);
|
||||
message.remove(0);
|
||||
|
||||
return method;
|
||||
} else {
|
||||
final int line = (message.size() > 0) ? message.get(0).getLineNumber() : 0;
|
||||
throw new BatchException("Missing http request line", BatchException.MessageKeys.INVALID_STATUS_LINE, "" + line);
|
||||
}
|
||||
}
|
||||
|
||||
public Line getHttpStatusLine() {
|
||||
return httpStatusLine;
|
||||
}
|
||||
|
||||
public List<Line> getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
public int getBodySize() {
|
||||
return bodySize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Header getHeaders() {
|
||||
return headers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStrict() {
|
||||
return isStrict;
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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 java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.olingo.server.api.ODataRequest;
|
||||
import org.apache.olingo.server.api.batch.BatchRequestPart;
|
||||
|
||||
public class BatchRequestPartImpl implements BatchRequestPart {
|
||||
|
||||
private List<ODataRequest> requests = new ArrayList<ODataRequest>();
|
||||
private boolean isChangeSet;
|
||||
|
||||
public BatchRequestPartImpl(final boolean isChangeSet, final List<ODataRequest> requests) {
|
||||
this.isChangeSet = isChangeSet;
|
||||
this.requests = requests;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChangeSet() {
|
||||
return isChangeSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ODataRequest> getRequests() {
|
||||
return Collections.unmodifiableList(requests);
|
||||
}
|
||||
}
|
@ -0,0 +1,286 @@
|
||||
/*
|
||||
* 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 java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class BufferedReaderIncludingLineEndings extends Reader {
|
||||
private static final char CR = '\r';
|
||||
private static final char LF = '\n';
|
||||
private static final int EOF = -1;
|
||||
private static final int BUFFER_SIZE = 8192;
|
||||
private Reader reader;
|
||||
private char[] buffer;
|
||||
private int offset = 0;
|
||||
private int limit = 0;
|
||||
|
||||
public BufferedReaderIncludingLineEndings(final Reader reader) {
|
||||
this(reader, BUFFER_SIZE);
|
||||
}
|
||||
|
||||
public BufferedReaderIncludingLineEndings(final Reader reader, final int bufferSize) {
|
||||
if (bufferSize <= 0) {
|
||||
throw new IllegalArgumentException("Buffer size must be greater than zero.");
|
||||
}
|
||||
|
||||
this.reader = reader;
|
||||
buffer = new char[bufferSize];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(final char[] charBuffer, final int bufferOffset, final int length) throws IOException {
|
||||
if ((bufferOffset + length) > charBuffer.length) {
|
||||
throw new IndexOutOfBoundsException("Buffer is too small");
|
||||
}
|
||||
|
||||
if (length < 0 || bufferOffset < 0) {
|
||||
throw new IndexOutOfBoundsException("Offset and length must be grater than zero");
|
||||
}
|
||||
|
||||
// Check if buffer is filled. Return if EOF is reached
|
||||
// Is buffer refill required
|
||||
if (limit == offset || isEOF()) {
|
||||
fillBuffer();
|
||||
|
||||
if (isEOF()) {
|
||||
return EOF;
|
||||
}
|
||||
}
|
||||
|
||||
int bytesRead = 0;
|
||||
int bytesToRead = length;
|
||||
int currentOutputOffset = bufferOffset;
|
||||
|
||||
while (bytesToRead != 0) {
|
||||
// Is buffer refill required?
|
||||
if (limit == offset) {
|
||||
fillBuffer();
|
||||
|
||||
if (isEOF()) {
|
||||
bytesToRead = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (bytesToRead > 0) {
|
||||
int readByte = Math.min(limit - offset, bytesToRead);
|
||||
bytesRead += readByte;
|
||||
bytesToRead -= readByte;
|
||||
|
||||
for (int i = 0; i < readByte; i++) {
|
||||
charBuffer[currentOutputOffset++] = buffer[offset++];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
public List<String> toList() throws IOException {
|
||||
final List<String> result = new ArrayList<String>();
|
||||
String currentLine;
|
||||
|
||||
while ((currentLine = readLine()) != null) {
|
||||
result.add(currentLine);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<Line> toLineList() throws IOException {
|
||||
final List<Line> result = new ArrayList<Line>();
|
||||
String currentLine;
|
||||
int counter = 1;
|
||||
|
||||
while ((currentLine = readLine()) != null) {
|
||||
result.add(new Line(currentLine, counter++));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public String readLine() throws IOException {
|
||||
if (limit == EOF) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final StringBuilder stringBuffer = new StringBuilder();
|
||||
boolean foundLineEnd = false; // EOF will be considered as line ending
|
||||
|
||||
while (!foundLineEnd) {
|
||||
// Is buffer refill required?
|
||||
if (limit == offset) {
|
||||
if (fillBuffer() == EOF) {
|
||||
foundLineEnd = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundLineEnd) {
|
||||
char currentChar = buffer[offset++];
|
||||
stringBuffer.append(currentChar);
|
||||
|
||||
if (currentChar == LF) {
|
||||
foundLineEnd = true;
|
||||
} else if (currentChar == CR) {
|
||||
foundLineEnd = true;
|
||||
|
||||
// Check next char. Consume \n if available
|
||||
// Is buffer refill required?
|
||||
if (limit == offset) {
|
||||
fillBuffer();
|
||||
}
|
||||
|
||||
// Check if there is at least one character
|
||||
if (limit != EOF && buffer[offset] == LF) {
|
||||
stringBuffer.append(LF);
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (stringBuffer.length() == 0) ? null : stringBuffer.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
reader.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean ready() throws IOException {
|
||||
// Not EOF and buffer refill is not required
|
||||
return !isEOF() && !(limit == offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() throws IOException {
|
||||
throw new IOException("Reset is not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mark(final int readAheadLimit) throws IOException {
|
||||
throw new IOException("Mark is not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean markSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(final long n) throws IOException {
|
||||
if (n == 0) {
|
||||
return 0;
|
||||
} else if (n < 0) {
|
||||
throw new IllegalArgumentException("skip value is negative");
|
||||
} else {
|
||||
long charactersToSkip = n;
|
||||
long charactersSkiped = 0;
|
||||
|
||||
while (charactersToSkip != 0) {
|
||||
// Is buffer refill required?
|
||||
if (limit == offset) {
|
||||
fillBuffer();
|
||||
|
||||
if (isEOF()) {
|
||||
charactersToSkip = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if more characters are available
|
||||
if (!isEOF()) {
|
||||
int skipChars = (int) Math.min(limit - offset, charactersToSkip);
|
||||
|
||||
charactersSkiped += skipChars;
|
||||
charactersToSkip -= skipChars;
|
||||
offset += skipChars;
|
||||
}
|
||||
}
|
||||
|
||||
return charactersSkiped;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isEOF() {
|
||||
return limit == EOF;
|
||||
}
|
||||
|
||||
private int fillBuffer() throws IOException {
|
||||
limit = reader.read(buffer, 0, buffer.length);
|
||||
offset = 0;
|
||||
|
||||
return limit;
|
||||
}
|
||||
|
||||
public static class Line {
|
||||
private final int lineNumber;
|
||||
private final String content;
|
||||
|
||||
public Line(final String content, final int lineNumber) {
|
||||
this.content = content;
|
||||
this.lineNumber = lineNumber;
|
||||
}
|
||||
|
||||
public int getLineNumber() {
|
||||
return lineNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((content == null) ? 0 : content.hashCode());
|
||||
result = prime * result + lineNumber;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Line other = (Line) obj;
|
||||
if (content == null) {
|
||||
if (other.content != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!content.equals(other.content)) {
|
||||
return false;
|
||||
}
|
||||
if (lineNumber != other.lineNumber) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,181 @@
|
||||
/*
|
||||
* 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 java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class Header implements Iterable<HeaderField> {
|
||||
private final Map<String, HeaderField> headers = new HashMap<String, HeaderField>();
|
||||
private int lineNumber;
|
||||
|
||||
public Header(final int lineNumer) {
|
||||
lineNumber = lineNumer;
|
||||
}
|
||||
|
||||
public void addHeader(final String name, final String value, final int lineNumber) {
|
||||
final HeaderField headerField = getHeaderFieldOrDefault(name, lineNumber);
|
||||
final List<String> headerValues = headerField.getValues();
|
||||
|
||||
if (!headerValues.contains(value)) {
|
||||
headerValues.add(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void addHeader(final String name, final List<String> values, final int lineNumber) {
|
||||
final HeaderField headerField = getHeaderFieldOrDefault(name, lineNumber);
|
||||
final List<String> headerValues = headerField.getValues();
|
||||
|
||||
for (final String value : values) {
|
||||
if (!headerValues.contains(value)) {
|
||||
headerValues.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void replaceHeaderField(final HeaderField headerField) {
|
||||
headers.put(headerField.getFieldName().toLowerCase(Locale.ENGLISH), headerField);
|
||||
}
|
||||
|
||||
public boolean exists(final String name) {
|
||||
final HeaderField field = headers.get(name.toLowerCase(Locale.ENGLISH));
|
||||
|
||||
return field != null && field.getValues().size() != 0;
|
||||
}
|
||||
|
||||
public boolean isHeaderMatching(final String name, final Pattern pattern) {
|
||||
if (getHeaders(name).size() != 1) {
|
||||
return false;
|
||||
} else {
|
||||
return pattern.matcher(getHeaders(name).get(0)).matches();
|
||||
}
|
||||
}
|
||||
|
||||
public void removeHeader(final String name) {
|
||||
headers.remove(name.toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
public String getHeader(final String name) {
|
||||
final HeaderField headerField = getHeaderField(name);
|
||||
|
||||
return (headerField == null) ? null : headerField.getValue();
|
||||
}
|
||||
|
||||
public String getHeaderNotNull(final String name) {
|
||||
final HeaderField headerField = getHeaderField(name);
|
||||
|
||||
return (headerField == null) ? "" : headerField.getValueNotNull();
|
||||
}
|
||||
|
||||
public List<String> getHeaders(final String name) {
|
||||
final HeaderField headerField = getHeaderField(name);
|
||||
|
||||
return (headerField == null) ? new ArrayList<String>() : headerField.getValues();
|
||||
}
|
||||
|
||||
public HeaderField getHeaderField(final String name) {
|
||||
return headers.get(name.toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
public int getLineNumber() {
|
||||
return lineNumber;
|
||||
}
|
||||
|
||||
public Map<String, String> toSingleMap() {
|
||||
final Map<String, String> singleMap = new HashMap<String, String>();
|
||||
|
||||
for (final String key : headers.keySet()) {
|
||||
HeaderField field = headers.get(key);
|
||||
singleMap.put(field.getFieldName(), getHeader(key));
|
||||
}
|
||||
|
||||
return singleMap;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> toMultiMap() {
|
||||
final Map<String, List<String>> singleMap = new HashMap<String, List<String>>();
|
||||
|
||||
for (final String key : headers.keySet()) {
|
||||
HeaderField field = headers.get(key);
|
||||
singleMap.put(field.getFieldName(), field.getValues());
|
||||
}
|
||||
|
||||
return singleMap;
|
||||
}
|
||||
|
||||
private HeaderField getHeaderFieldOrDefault(final String name, final int lineNumber) {
|
||||
HeaderField headerField = headers.get(name.toLowerCase(Locale.ENGLISH));
|
||||
|
||||
if (headerField == null) {
|
||||
headerField = new HeaderField(name, lineNumber);
|
||||
headers.put(name.toLowerCase(Locale.ENGLISH), headerField);
|
||||
}
|
||||
|
||||
return headerField;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Header clone() {
|
||||
final Header newInstance = new Header(lineNumber);
|
||||
|
||||
for (final String key : headers.keySet()) {
|
||||
newInstance.headers.put(key, headers.get(key).clone());
|
||||
}
|
||||
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<HeaderField> iterator() {
|
||||
return new Iterator<HeaderField>() {
|
||||
Iterator<String> keyIterator = headers.keySet().iterator();
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return keyIterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HeaderField next() {
|
||||
return headers.get(keyIterator.next());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static List<String> splitValuesByComma(final String headerValue) {
|
||||
final List<String> singleValues = new ArrayList<String>();
|
||||
|
||||
String[] parts = headerValue.split(",");
|
||||
for (final String value : parts) {
|
||||
singleValues.add(value.trim());
|
||||
}
|
||||
|
||||
return singleValues;
|
||||
}
|
||||
}
|
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class HeaderField implements Cloneable {
|
||||
private final String fieldName;
|
||||
private final List<String> values;
|
||||
private final int lineNumber;
|
||||
|
||||
public HeaderField(final String fieldName, final int lineNumber) {
|
||||
this(fieldName, new ArrayList<String>(), lineNumber);
|
||||
}
|
||||
|
||||
public HeaderField(final String fieldName, final List<String> values, final int lineNumber) {
|
||||
this.fieldName = fieldName;
|
||||
this.values = values;
|
||||
this.lineNumber = lineNumber;
|
||||
}
|
||||
|
||||
public String getFieldName() {
|
||||
return fieldName;
|
||||
}
|
||||
|
||||
public List<String> getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
final StringBuilder result = new StringBuilder();
|
||||
|
||||
for (final String value : values) {
|
||||
result.append(value);
|
||||
result.append(", ");
|
||||
}
|
||||
|
||||
if (result.length() > 0) {
|
||||
result.delete(result.length() - 2, result.length());
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public String getValueNotNull() {
|
||||
final String value = getValue();
|
||||
|
||||
return (value == null) ? "" : value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HeaderField clone() {
|
||||
List<String> newValues = new ArrayList<String>();
|
||||
newValues.addAll(values);
|
||||
|
||||
return new HeaderField(fieldName, newValues, lineNumber);
|
||||
}
|
||||
|
||||
public int getLineNumber() {
|
||||
return lineNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((fieldName == null) ? 0 : fieldName.hashCode());
|
||||
result = prime * result + lineNumber;
|
||||
result = prime * result + ((values == null) ? 0 : values.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
HeaderField other = (HeaderField) obj;
|
||||
if (fieldName == null) {
|
||||
if (other.fieldName != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!fieldName.equals(other.fieldName)) {
|
||||
return false;
|
||||
}
|
||||
if (lineNumber != other.lineNumber) {
|
||||
return false;
|
||||
}
|
||||
if (values == null) {
|
||||
if (other.values != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!values.equals(other.values)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* 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.transformator;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
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.BatchParserResult;
|
||||
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.BatchBodyPart;
|
||||
import org.apache.olingo.server.core.batch.parser.BatchChangeSetPart;
|
||||
import org.apache.olingo.server.core.batch.parser.BatchParserCommon;
|
||||
import org.apache.olingo.server.core.batch.parser.BatchPart;
|
||||
import org.apache.olingo.server.core.batch.parser.BatchQueryOperation;
|
||||
import org.apache.olingo.server.core.batch.parser.BatchRequestPartImpl;
|
||||
import org.apache.olingo.server.core.batch.parser.Header;
|
||||
import org.apache.olingo.server.core.batch.parser.HeaderField;
|
||||
import org.apache.olingo.server.core.batch.transformator.BatchTransformatorCommon.HttpRequestStatusLine;
|
||||
|
||||
public class BatchRequestTransformator implements BatchTransformator {
|
||||
private final String baseUri;
|
||||
private final String rawServiceResolutionUri;
|
||||
|
||||
public BatchRequestTransformator(final String baseUri, final String serviceResolutionUri) {
|
||||
this.baseUri = baseUri;
|
||||
this.rawServiceResolutionUri = serviceResolutionUri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BatchParserResult> transform(final BatchBodyPart bodyPart) throws BatchException {
|
||||
final List<ODataRequest> requests = new LinkedList<ODataRequest>();
|
||||
final List<BatchParserResult> resultList = new ArrayList<BatchParserResult>();
|
||||
|
||||
validateBodyPartHeader(bodyPart);
|
||||
|
||||
for (BatchQueryOperation queryOperation : bodyPart.getRequests()) {
|
||||
requests.add(processQueryOperation(bodyPart, baseUri, queryOperation));
|
||||
}
|
||||
|
||||
resultList.add(new BatchRequestPartImpl(bodyPart.isChangeSet(), requests));
|
||||
return resultList;
|
||||
}
|
||||
|
||||
private ODataRequest
|
||||
processQueryOperation(BatchBodyPart bodyPart, String baseUri, BatchQueryOperation queryOperation)
|
||||
throws BatchException {
|
||||
if (bodyPart.isChangeSet()) {
|
||||
BatchQueryOperation encapsulatedQueryOperation = ((BatchChangeSetPart) queryOperation).getRequest();
|
||||
handleContentId(queryOperation, encapsulatedQueryOperation);
|
||||
validateHeader(queryOperation, true);
|
||||
|
||||
return createRequest(encapsulatedQueryOperation, baseUri, bodyPart.isChangeSet());
|
||||
} else {
|
||||
return createRequest(queryOperation, baseUri, bodyPart.isChangeSet());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleContentId(BatchQueryOperation changeRequestPart, BatchQueryOperation request)
|
||||
throws BatchException {
|
||||
final HeaderField contentIdChangeRequestPart = getContentId(changeRequestPart);
|
||||
final HeaderField contentIdRequest = getContentId(request);
|
||||
|
||||
if (contentIdChangeRequestPart == null && contentIdRequest == null) {
|
||||
throw new BatchException("Missing content type", MessageKeys.MISSING_CONTENT_ID, changeRequestPart.getHeaders()
|
||||
.getLineNumber());
|
||||
} else if (contentIdChangeRequestPart != null) {
|
||||
request.getHeaders().replaceHeaderField(contentIdChangeRequestPart);
|
||||
}
|
||||
}
|
||||
|
||||
private HeaderField getContentId(final BatchQueryOperation queryOperation) throws BatchException {
|
||||
final HeaderField contentTypeHeader = queryOperation.getHeaders().getHeaderField(BatchParserCommon.HTTP_CONTENT_ID);
|
||||
|
||||
if (contentTypeHeader != null) {
|
||||
if (contentTypeHeader.getValues().size() == 1) {
|
||||
return contentTypeHeader;
|
||||
} else {
|
||||
throw new BatchException("Invalid header", MessageKeys.INVALID_HEADER, contentTypeHeader.getLineNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private ODataRequest createRequest(BatchQueryOperation operation, String baseUri, boolean isChangeSet)
|
||||
throws BatchException {
|
||||
final HttpRequestStatusLine statusLine =
|
||||
new HttpRequestStatusLine(operation.getHttpStatusLine(), baseUri, rawServiceResolutionUri, operation
|
||||
.getHeaders());
|
||||
statusLine.validateHttpMethod(isChangeSet);
|
||||
|
||||
validateBody(statusLine, operation);
|
||||
InputStream bodyStrean = getBodyStream(operation, statusLine);
|
||||
|
||||
validateForbiddenHeader(operation);
|
||||
|
||||
final ODataRequest request = new ODataRequest();
|
||||
request.setBody(bodyStrean);
|
||||
request.setMethod(statusLine.getMethod());
|
||||
request.setRawBaseUri(statusLine.getRawBaseUri());
|
||||
request.setRawODataPath(statusLine.getRawODataPath());
|
||||
request.setRawQueryPath(statusLine.getRawQueryPath());
|
||||
request.setRawRequestUri(statusLine.getRawRequestUri());
|
||||
request.setRawServiceResolutionUri(statusLine.getRawServiceResolutionUri());
|
||||
|
||||
for (final HeaderField field : operation.getHeaders()) {
|
||||
request.addHeader(field.getFieldName(), field.getValues());
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
private void validateForbiddenHeader(BatchQueryOperation operation) throws BatchException {
|
||||
final Header header = operation.getHeaders();
|
||||
|
||||
if (header.exists(HttpHeader.AUTHORIZATION) || header.exists(BatchParserCommon.HTTP_EXPECT)
|
||||
|| header.exists(BatchParserCommon.HTTP_FROM) || header.exists(BatchParserCommon.HTTP_MAX_FORWARDS)
|
||||
|| header.exists(BatchParserCommon.HTTP_RANGE) || header.exists(BatchParserCommon.HTTP_TE)) {
|
||||
throw new BatchException("Forbidden header", MessageKeys.FORBIDDEN_HEADER, header.getLineNumber());
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream getBodyStream(BatchQueryOperation operation, HttpRequestStatusLine statusLine)
|
||||
throws BatchException {
|
||||
if (statusLine.getMethod().equals(HttpMethod.GET)) {
|
||||
return new ByteArrayInputStream(new byte[0]);
|
||||
} else {
|
||||
int contentLength = BatchTransformatorCommon.getContentLength(operation.getHeaders());
|
||||
|
||||
if (contentLength == -1) {
|
||||
return BatchParserCommon.convertLineListToInputStream(operation.getBody());
|
||||
} else {
|
||||
return BatchParserCommon.convertLineListToInputStream(operation.getBody(), contentLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validateBody(HttpRequestStatusLine statusLine, BatchQueryOperation operation) throws BatchException {
|
||||
if (statusLine.getMethod().equals(HttpMethod.GET) && isUnvalidGetRequestBody(operation)) {
|
||||
throw new BatchException("Invalid request line", MessageKeys.INVALID_CONTENT, statusLine.getLineNumber());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isUnvalidGetRequestBody(final BatchQueryOperation operation) {
|
||||
return (operation.getBody().size() > 1)
|
||||
|| (operation.getBody().size() == 1 && !"".equals(operation.getBody().get(0).toString().trim()));
|
||||
}
|
||||
|
||||
private void validateHeader(BatchPart bodyPart, boolean isChangeSet) throws BatchException {
|
||||
final Header headers = bodyPart.getHeaders();
|
||||
|
||||
BatchTransformatorCommon.validateContentType(headers, BatchParserCommon.PATTERN_CONTENT_TYPE_APPLICATION_HTTP);
|
||||
if (isChangeSet) {
|
||||
BatchTransformatorCommon.validateContentTransferEncoding(headers);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateBodyPartHeader(BatchBodyPart bodyPart) throws BatchException {
|
||||
final Header header = bodyPart.getHeaders();
|
||||
|
||||
if (bodyPart.isChangeSet()) {
|
||||
BatchTransformatorCommon.validateContentType(header, BatchParserCommon.PATTERN_MULTIPART_BOUNDARY);
|
||||
} else {
|
||||
BatchTransformatorCommon.validateContentTransferEncoding(header);
|
||||
BatchTransformatorCommon.validateContentType(header, BatchParserCommon.PATTERN_CONTENT_TYPE_APPLICATION_HTTP);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.transformator;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.olingo.server.api.batch.BatchParserResult;
|
||||
import org.apache.olingo.server.core.batch.BatchException;
|
||||
import org.apache.olingo.server.core.batch.parser.BatchBodyPart;
|
||||
|
||||
public interface BatchTransformator {
|
||||
public List<BatchParserResult> transform(BatchBodyPart bodyPart) throws BatchException;
|
||||
}
|
@ -0,0 +1,250 @@
|
||||
/*
|
||||
* 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.transformator;
|
||||
|
||||
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.commons.api.http.HttpContentType;
|
||||
import org.apache.olingo.commons.api.http.HttpHeader;
|
||||
import org.apache.olingo.commons.api.http.HttpMethod;
|
||||
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.batch.parser.BufferedReaderIncludingLineEndings.Line;
|
||||
import org.apache.olingo.server.core.batch.parser.Header;
|
||||
import org.apache.olingo.server.core.batch.parser.HeaderField;
|
||||
|
||||
public class BatchTransformatorCommon {
|
||||
|
||||
public static void validateContentType(final Header headers, final Pattern pattern) throws BatchException {
|
||||
List<String> contentTypes = headers.getHeaders(HttpHeader.CONTENT_TYPE);
|
||||
|
||||
if (contentTypes.size() == 0) {
|
||||
throw new BatchException("Missing content type", MessageKeys.MISSING_CONTENT_TYPE, headers.getLineNumber());
|
||||
}
|
||||
if (!headers.isHeaderMatching(HttpHeader.CONTENT_TYPE, pattern)) {
|
||||
|
||||
throw new BatchException("Invalid content type", MessageKeys.INVALID_CONTENT_TYPE,
|
||||
HttpContentType.MULTIPART_MIXED + " or " + HttpContentType.APPLICATION_HTTP);
|
||||
}
|
||||
}
|
||||
|
||||
public static void validateContentTransferEncoding(Header headers) throws BatchException {
|
||||
final HeaderField contentTransferField = headers.getHeaderField(BatchParserCommon.HTTP_CONTENT_TRANSFER_ENCODING);
|
||||
|
||||
if (contentTransferField != null) {
|
||||
final List<String> contentTransferValues = contentTransferField.getValues();
|
||||
if (contentTransferValues.size() == 1) {
|
||||
String encoding = contentTransferValues.get(0);
|
||||
|
||||
if (!BatchParserCommon.BINARY_ENCODING.equalsIgnoreCase(encoding)) {
|
||||
throw new BatchException("Invalid content transfer encoding", MessageKeys.INVALID_CONTENT_TRANSFER_ENCODING,
|
||||
headers.getLineNumber());
|
||||
}
|
||||
} else {
|
||||
throw new BatchException("Invalid header", MessageKeys.INVALID_HEADER, headers.getLineNumber());
|
||||
}
|
||||
} else {
|
||||
throw new BatchException("Missing mandatory content transfer encoding",
|
||||
MessageKeys.MISSING_CONTENT_TRANSFER_ENCODING,
|
||||
headers.getLineNumber());
|
||||
}
|
||||
}
|
||||
|
||||
public static int getContentLength(Header headers) throws BatchException {
|
||||
final HeaderField contentLengthField = headers.getHeaderField(HttpHeader.CONTENT_LENGTH);
|
||||
|
||||
if (contentLengthField != null && contentLengthField.getValues().size() == 1) {
|
||||
final List<String> contentLengthValues = contentLengthField.getValues();
|
||||
|
||||
try {
|
||||
int contentLength = Integer.parseInt(contentLengthValues.get(0));
|
||||
|
||||
if (contentLength < 0) {
|
||||
throw new BatchException("Invalid content length", MessageKeys.INVALID_CONTENT_LENGTH, contentLengthField
|
||||
.getLineNumber());
|
||||
}
|
||||
|
||||
return contentLength;
|
||||
} catch (NumberFormatException e) {
|
||||
throw new BatchException("Invalid header", MessageKeys.INVALID_HEADER, contentLengthField.getLineNumber());
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static class HttpRequestStatusLine {
|
||||
private static final Pattern PATTERN_RELATIVE_URI = Pattern.compile("([^/][^?]*)(?:\\?(.*))?");
|
||||
private static final Pattern PATTERN_ABSOLUTE_URI_WITH_HOST = Pattern.compile("(/[^?]*)(?:\\?(.*))?");
|
||||
private static final Pattern PATTERN_ABSOLUTE_URI = Pattern.compile("(http[s]?://[^?]*)(?:\\?(.*))?");
|
||||
|
||||
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;
|
||||
|
||||
private HttpMethod method;
|
||||
private String httpVersion;
|
||||
private String rawServiceResolutionUri;
|
||||
private String rawQueryPath;
|
||||
private String rawODataPath;
|
||||
private String rawBaseUri;
|
||||
private Header header;
|
||||
private String rawRequestUri;
|
||||
|
||||
public HttpRequestStatusLine(final Line httpStatusLine, final String baseUri, final String serviceResolutionUri,
|
||||
final Header requestHeader)
|
||||
throws BatchException {
|
||||
statusLine = httpStatusLine;
|
||||
requestBaseUri = baseUri;
|
||||
header = requestHeader;
|
||||
rawServiceResolutionUri = serviceResolutionUri;
|
||||
|
||||
parse();
|
||||
}
|
||||
|
||||
private void parse() throws BatchException {
|
||||
final String[] parts = statusLine.toString().split(" ");
|
||||
|
||||
if (parts.length == 3) {
|
||||
method = parseMethod(parts[0]);
|
||||
parseRawPaths(parts[1]);
|
||||
httpVersion = parseHttpVersion(parts[2]);
|
||||
} else {
|
||||
throw new BatchException("Invalid status line", MessageKeys.INVALID_STATUS_LINE, statusLine.getLineNumber());
|
||||
}
|
||||
}
|
||||
|
||||
private HttpMethod parseMethod(final String method) throws BatchException {
|
||||
try {
|
||||
return HttpMethod.valueOf(method.trim());
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new BatchException("Illegal http method", MessageKeys.INVALID_METHOD, statusLine.getLineNumber());
|
||||
}
|
||||
}
|
||||
|
||||
private void parseRawPaths(final String rawUrl) throws BatchException {
|
||||
final Matcher absoluteUriMatcher = PATTERN_ABSOLUTE_URI.matcher(rawUrl);
|
||||
final Matcher absoluteUriWtithHostMatcher = PATTERN_ABSOLUTE_URI_WITH_HOST.matcher(rawUrl);
|
||||
final Matcher relativeUriMatcher = PATTERN_RELATIVE_URI.matcher(rawUrl);
|
||||
|
||||
if (absoluteUriMatcher.matches()) {
|
||||
buildUri(absoluteUriMatcher.group(1), absoluteUriMatcher.group(2));
|
||||
|
||||
} else if (absoluteUriWtithHostMatcher.matches()) {
|
||||
final List<String> hostHeader = header.getHeaders(HttpHeader.HOST);
|
||||
if (hostHeader.size() == 1) {
|
||||
buildUri(hostHeader.get(0) + absoluteUriWtithHostMatcher.group(1), absoluteUriWtithHostMatcher.group(2));
|
||||
} else {
|
||||
throw new BatchException("Exactly one host header is required", MessageKeys.MISSING_MANDATORY_HEADER,
|
||||
statusLine.getLineNumber());
|
||||
}
|
||||
|
||||
} else if (relativeUriMatcher.matches()) {
|
||||
buildUri(requestBaseUri + "/" + relativeUriMatcher.group(1), relativeUriMatcher.group(2));
|
||||
|
||||
} else {
|
||||
throw new BatchException("Invalid uri", MessageKeys.INVALID_URI, statusLine.getLineNumber());
|
||||
}
|
||||
}
|
||||
|
||||
private void buildUri(final String resourceUri, final String queryOptions) throws BatchException {
|
||||
if(!resourceUri.startsWith(requestBaseUri)) {
|
||||
throw new BatchException("Host do not match", MessageKeys.INVALID_URI, statusLine.getLineNumber());
|
||||
}
|
||||
|
||||
final int oDataPathIndex = resourceUri.indexOf(requestBaseUri);
|
||||
|
||||
rawBaseUri = requestBaseUri;
|
||||
rawODataPath = resourceUri.substring(oDataPathIndex + requestBaseUri.length());
|
||||
rawServiceResolutionUri = "";
|
||||
rawRequestUri = requestBaseUri + rawODataPath;
|
||||
|
||||
if (queryOptions != null) {
|
||||
rawRequestUri += "?" + queryOptions;
|
||||
rawQueryPath = queryOptions;
|
||||
} else {
|
||||
rawQueryPath = "";
|
||||
}
|
||||
}
|
||||
|
||||
private String parseHttpVersion(final String httpVersion) throws BatchException {
|
||||
if (!HTTP_VERSION.equals(httpVersion.trim())) {
|
||||
throw new BatchException("Invalid http version", MessageKeys.INVALID_HTTP_VERSION, 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("Invalid change set method", MessageKeys.INVALID_CHANGESET_METHOD, statusLine
|
||||
.getLineNumber());
|
||||
} else {
|
||||
throw new BatchException("Invalid query operation method", MessageKeys.INVALID_QUERY_OPERATION_METHOD,
|
||||
statusLine.getLineNumber());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public HttpMethod getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public String getHttpVersion() {
|
||||
return httpVersion;
|
||||
}
|
||||
|
||||
public int getLineNumber() {
|
||||
return statusLine.getLineNumber();
|
||||
}
|
||||
|
||||
public String getRawBaseUri() {
|
||||
return rawBaseUri;
|
||||
}
|
||||
|
||||
public String getRawODataPath() {
|
||||
return rawODataPath;
|
||||
}
|
||||
|
||||
public String getRawQueryPath() {
|
||||
return rawQueryPath;
|
||||
}
|
||||
|
||||
public String getRawRequestUri() {
|
||||
return rawRequestUri;
|
||||
}
|
||||
|
||||
public String getRawServiceResolutionUri() {
|
||||
return rawServiceResolutionUri;
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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'.
|
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.olingo.server.core.batch.parser;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.olingo.commons.api.http.HttpContentType;
|
||||
import org.apache.olingo.commons.api.http.HttpHeader;
|
||||
import org.apache.olingo.server.core.batch.parser.BatchParserCommon;
|
||||
import org.apache.olingo.server.core.batch.parser.Header;
|
||||
import org.junit.Test;
|
||||
|
||||
public class HeaderTest {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
Header header = new Header(1);
|
||||
header.addHeader(HttpHeader.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED, 1);
|
||||
|
||||
assertEquals(HttpContentType.MULTIPART_MIXED, header.getHeader(HttpHeader.CONTENT_TYPE));
|
||||
assertEquals(1, header.getHeaders(HttpHeader.CONTENT_TYPE).size());
|
||||
assertEquals(HttpContentType.MULTIPART_MIXED, header.getHeaders(HttpHeader.CONTENT_TYPE).get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotAvailable() {
|
||||
Header header = new Header(1);
|
||||
|
||||
assertNull(header.getHeader(HttpHeader.CONTENT_TYPE));
|
||||
assertEquals(0, header.getHeaders(HttpHeader.CONTENT_TYPE).size());
|
||||
assertEquals("", header.getHeaderNotNull(HttpHeader.CONTENT_TYPE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCaseInsensitive() {
|
||||
Header header = new Header(1);
|
||||
header.addHeader(HttpHeader.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED, 1);
|
||||
|
||||
assertEquals(HttpContentType.MULTIPART_MIXED, header.getHeader("cOnTenT-TyPE"));
|
||||
assertEquals(1, header.getHeaders("cOnTenT-TyPE").size());
|
||||
assertEquals(HttpContentType.MULTIPART_MIXED, header.getHeaders("cOnTenT-TyPE").get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDuplicatedAdd() {
|
||||
Header header = new Header(1);
|
||||
header.addHeader(HttpHeader.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED, 1);
|
||||
header.addHeader(HttpHeader.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED, 2);
|
||||
|
||||
assertEquals(HttpContentType.MULTIPART_MIXED, header.getHeader(HttpHeader.CONTENT_TYPE));
|
||||
assertEquals(1, header.getHeaders(HttpHeader.CONTENT_TYPE).size());
|
||||
assertEquals(HttpContentType.MULTIPART_MIXED, header.getHeaders(HttpHeader.CONTENT_TYPE).get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatcher() {
|
||||
Header header = new Header(1);
|
||||
header.addHeader(HttpHeader.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED + ";boundary=123", 1);
|
||||
|
||||
assertTrue(header.isHeaderMatching(HttpHeader.CONTENT_TYPE, BatchParserCommon.PATTERN_MULTIPART_BOUNDARY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFieldName() {
|
||||
Header header = new Header(0);
|
||||
header.addHeader("MyFieldNamE", "myValue", 1);
|
||||
|
||||
assertEquals("MyFieldNamE", header.getHeaderField("myfieldname").getFieldName());
|
||||
assertEquals("MyFieldNamE", header.toSingleMap().keySet().toArray(new String[0])[0]);
|
||||
assertEquals("MyFieldNamE", header.toMultiMap().keySet().toArray(new String[0])[0]);
|
||||
|
||||
assertEquals("myValue", header.toMultiMap().get("MyFieldNamE").get(0));
|
||||
assertEquals("myValue", header.toSingleMap().get("MyFieldNamE"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeepCopy() {
|
||||
Header header = new Header(1);
|
||||
header.addHeader(HttpHeader.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED + ";boundary=123", 1);
|
||||
|
||||
Header copy = header.clone();
|
||||
assertEquals(header.getHeaders(HttpHeader.CONTENT_TYPE), copy.getHeaders(HttpHeader.CONTENT_TYPE));
|
||||
assertEquals(header.getHeader(HttpHeader.CONTENT_TYPE), copy.getHeader(HttpHeader.CONTENT_TYPE));
|
||||
assertEquals(header.getHeaderField(HttpHeader.CONTENT_TYPE), copy.getHeaderField(HttpHeader.CONTENT_TYPE));
|
||||
|
||||
assertTrue(header.getHeaders(HttpHeader.CONTENT_TYPE) != copy.getHeaders(HttpHeader.CONTENT_TYPE));
|
||||
assertTrue(header.getHeaderField(HttpHeader.CONTENT_TYPE) != copy.getHeaderField(HttpHeader.CONTENT_TYPE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatcherNoHeader() {
|
||||
Header header = new Header(1);
|
||||
|
||||
assertFalse(header.isHeaderMatching(HttpHeader.CONTENT_TYPE, BatchParserCommon.PATTERN_MULTIPART_BOUNDARY));
|
||||
}
|
||||
|
||||
// @Test
|
||||
// public void testMatcherFail() {
|
||||
// Header header = new Header(1);
|
||||
// header.addHeader(HttpHeader.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED + ";boundary=123", 1);
|
||||
//
|
||||
// assertFalse(header.isHeaderMatching(HttpHeader.CONTENT_TYPE, BatchParserCommon.PATTERN_HEADER_LINE));
|
||||
// }
|
||||
|
||||
@Test
|
||||
public void testDuplicatedAddList() {
|
||||
Header header = new Header(1);
|
||||
header.addHeader(HttpHeader.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED, 1);
|
||||
header.addHeader(HttpHeader.CONTENT_TYPE, Arrays.asList(new String[] { HttpContentType.MULTIPART_MIXED,
|
||||
HttpContentType.APPLICATION_ATOM_SVC }), 2);
|
||||
|
||||
assertEquals(HttpContentType.MULTIPART_MIXED + ", " + HttpContentType.APPLICATION_ATOM_SVC, header
|
||||
.getHeader(HttpHeader.CONTENT_TYPE));
|
||||
assertEquals(2, header.getHeaders(HttpHeader.CONTENT_TYPE).size());
|
||||
assertEquals(HttpContentType.MULTIPART_MIXED, header.getHeaders(HttpHeader.CONTENT_TYPE).get(0));
|
||||
assertEquals(HttpContentType.APPLICATION_ATOM_SVC, header.getHeaders(HttpHeader.CONTENT_TYPE).get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemove() {
|
||||
Header header = new Header(1);
|
||||
header.addHeader(HttpHeader.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED, 1);
|
||||
header.removeHeader(HttpHeader.CONTENT_TYPE);
|
||||
|
||||
assertNull(header.getHeader(HttpHeader.CONTENT_TYPE));
|
||||
assertEquals(0, header.getHeaders(HttpHeader.CONTENT_TYPE).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleValues() {
|
||||
Header header = new Header(1);
|
||||
header.addHeader(HttpHeader.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED, 1);
|
||||
header.addHeader(HttpHeader.CONTENT_TYPE, HttpContentType.APPLICATION_ATOM_SVC, 2);
|
||||
header.addHeader(HttpHeader.CONTENT_TYPE, HttpContentType.APPLICATION_ATOM_XML, 3);
|
||||
|
||||
final String fullHeaderString =
|
||||
HttpContentType.MULTIPART_MIXED + ", " + HttpContentType.APPLICATION_ATOM_SVC + ", "
|
||||
+ HttpContentType.APPLICATION_ATOM_XML;
|
||||
|
||||
assertEquals(fullHeaderString, header.getHeader(HttpHeader.CONTENT_TYPE));
|
||||
assertEquals(3, header.getHeaders(HttpHeader.CONTENT_TYPE).size());
|
||||
assertEquals(HttpContentType.MULTIPART_MIXED, header.getHeaders(HttpHeader.CONTENT_TYPE).get(0));
|
||||
assertEquals(HttpContentType.APPLICATION_ATOM_SVC, header.getHeaders(HttpHeader.CONTENT_TYPE).get(1));
|
||||
assertEquals(HttpContentType.APPLICATION_ATOM_XML, header.getHeaders(HttpHeader.CONTENT_TYPE).get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSplitValues() {
|
||||
final String values = "abc, def,123,77, 99, ysd";
|
||||
List<String> splittedValues = Header.splitValuesByComma(values);
|
||||
|
||||
assertEquals(6, splittedValues.size());
|
||||
assertEquals("abc", splittedValues.get(0));
|
||||
assertEquals("def", splittedValues.get(1));
|
||||
assertEquals("123", splittedValues.get(2));
|
||||
assertEquals("77", splittedValues.get(3));
|
||||
assertEquals("99", splittedValues.get(4));
|
||||
assertEquals("ysd", splittedValues.get(5));
|
||||
}
|
||||
}
|
@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.olingo.server.core.batch.writer;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
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.StringUtil;
|
||||
import org.apache.olingo.server.core.batch.parser.BatchParserCommon;
|
||||
import org.apache.olingo.server.core.batch.parser.BufferedReaderIncludingLineEndings;
|
||||
import org.junit.Test;
|
||||
|
||||
public class BatchResponseWriterTest {
|
||||
private static final String CRLF = "\r\n";
|
||||
|
||||
@Test
|
||||
public void testBatchResponse() throws IOException, BatchException {
|
||||
final List<ODataResponsePart> parts = new ArrayList<ODataResponsePart>();
|
||||
ODataResponse response = new ODataResponse();
|
||||
response.setStatusCode(HttpStatusCode.OK.getStatusCode());
|
||||
response.setHeader(HttpHeader.CONTENT_TYPE, "application/json");
|
||||
response.setContent(StringUtil.toInputStream("Walter Winter" + CRLF));
|
||||
|
||||
List<ODataResponse> responses = new ArrayList<ODataResponse>(1);
|
||||
responses.add(response);
|
||||
parts.add(new ODataResponsePartImpl(responses, false));
|
||||
|
||||
ODataResponse changeSetResponse = new ODataResponse();
|
||||
changeSetResponse.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode());
|
||||
changeSetResponse.setHeader(BatchParserCommon.HTTP_CONTENT_ID, "1");
|
||||
responses = new ArrayList<ODataResponse>(1);
|
||||
responses.add(changeSetResponse);
|
||||
parts.add(new ODataResponsePartImpl(responses, true));
|
||||
|
||||
BatchResponseWriter writer = new BatchResponseWriter();
|
||||
ODataResponse batchResponse = new ODataResponse();
|
||||
writer.toODataResponse(parts, batchResponse);
|
||||
|
||||
assertEquals(202, batchResponse.getStatusCode());
|
||||
assertNotNull(batchResponse.getContent());
|
||||
final BufferedReaderIncludingLineEndings reader =
|
||||
new BufferedReaderIncludingLineEndings(new InputStreamReader(batchResponse.getContent()));
|
||||
final List<String> body = reader.toList();
|
||||
reader.close();
|
||||
|
||||
int line = 0;
|
||||
assertEquals(25, body.size());
|
||||
assertTrue(body.get(line++).contains("--batch_"));
|
||||
assertEquals("Content-Type: application/http" + CRLF, body.get(line++));
|
||||
assertEquals("Content-Transfer-Encoding: binary" + CRLF, body.get(line++));
|
||||
assertEquals(CRLF, body.get(line++));
|
||||
assertEquals("HTTP/1.1 200 OK" + CRLF, body.get(line++));
|
||||
assertEquals("Content-Type: application/json" + CRLF, body.get(line++));
|
||||
assertEquals("Content-Length: 15" + CRLF, body.get(line++));
|
||||
assertEquals(CRLF, body.get(line++));
|
||||
assertEquals("Walter Winter" + CRLF, body.get(line++));
|
||||
assertEquals(CRLF, body.get(line++));
|
||||
assertTrue(body.get(line++).contains("--batch_"));
|
||||
assertTrue(body.get(line++).contains("Content-Type: multipart/mixed; boundary=changeset_"));
|
||||
assertEquals(CRLF, body.get(line++));
|
||||
assertTrue(body.get(line++).contains("--changeset_"));
|
||||
assertEquals("Content-Type: application/http" + CRLF, body.get(line++));
|
||||
assertEquals("Content-Transfer-Encoding: binary" + CRLF, body.get(line++));
|
||||
assertEquals("Content-Id: 1" + CRLF, body.get(line++));
|
||||
assertEquals(CRLF, body.get(line++));
|
||||
assertEquals("HTTP/1.1 204 No Content" + CRLF, body.get(line++));
|
||||
assertEquals("Content-Length: 0" + CRLF, body.get(line++));
|
||||
assertEquals(CRLF, body.get(line++));
|
||||
assertEquals(CRLF, body.get(line++));
|
||||
assertTrue(body.get(line++).contains("--changeset_"));
|
||||
assertEquals(CRLF, body.get(line++));
|
||||
assertTrue(body.get(line++).contains("--batch_"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponse() throws IOException, BatchException {
|
||||
List<ODataResponsePart> parts = new ArrayList<ODataResponsePart>();
|
||||
ODataResponse response = new ODataResponse();
|
||||
response.setStatusCode(HttpStatusCode.OK.getStatusCode());
|
||||
response.setHeader(HttpHeader.CONTENT_TYPE, "application/json");
|
||||
response.setContent(StringUtil.toInputStream("Walter Winter"));
|
||||
|
||||
List<ODataResponse> responses = new ArrayList<ODataResponse>(1);
|
||||
responses.add(response);
|
||||
parts.add(new ODataResponsePartImpl(responses, false));
|
||||
|
||||
ODataResponse batchResponse = new ODataResponse();
|
||||
new BatchResponseWriter().toODataResponse(parts, batchResponse);
|
||||
|
||||
assertEquals(202, batchResponse.getStatusCode());
|
||||
assertNotNull(batchResponse.getContent());
|
||||
final BufferedReaderIncludingLineEndings reader =
|
||||
new BufferedReaderIncludingLineEndings(new InputStreamReader(batchResponse.getContent()));
|
||||
final List<String> body = reader.toList();
|
||||
reader.close();
|
||||
|
||||
int line = 0;
|
||||
assertEquals(10, body.size());
|
||||
assertTrue(body.get(line++).contains("--batch_"));
|
||||
assertEquals("Content-Type: application/http" + CRLF, body.get(line++));
|
||||
assertEquals("Content-Transfer-Encoding: binary" + CRLF, body.get(line++));
|
||||
assertEquals(CRLF, body.get(line++));
|
||||
assertEquals("HTTP/1.1 200 OK" + CRLF, body.get(line++));
|
||||
assertEquals("Content-Type: application/json" + CRLF, body.get(line++));
|
||||
assertEquals("Content-Length: 13" + CRLF, body.get(line++));
|
||||
assertEquals(CRLF, body.get(line++));
|
||||
assertEquals("Walter Winter" + CRLF, body.get(line++));
|
||||
assertTrue(body.get(line++).contains("--batch_"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangeSetResponse() throws IOException, BatchException {
|
||||
List<ODataResponsePart> parts = new ArrayList<ODataResponsePart>();
|
||||
ODataResponse response = new ODataResponse();
|
||||
response.setHeader(BatchParserCommon.HTTP_CONTENT_ID, "1");
|
||||
response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode());
|
||||
|
||||
List<ODataResponse> responses = new ArrayList<ODataResponse>(1);
|
||||
responses.add(response);
|
||||
parts.add(new ODataResponsePartImpl(responses, true));
|
||||
|
||||
BatchResponseWriter writer = new BatchResponseWriter();
|
||||
ODataResponse batchResponse = new ODataResponse();
|
||||
writer.toODataResponse(parts, batchResponse);
|
||||
|
||||
assertEquals(202, batchResponse.getStatusCode());
|
||||
assertNotNull(batchResponse.getContent());
|
||||
|
||||
final BufferedReaderIncludingLineEndings reader =
|
||||
new BufferedReaderIncludingLineEndings(new InputStreamReader(batchResponse.getContent()));
|
||||
final List<String> body = reader.toList();
|
||||
reader.close();
|
||||
|
||||
int line = 0;
|
||||
assertEquals(15, body.size());
|
||||
assertTrue(body.get(line++).contains("--batch_"));
|
||||
assertTrue(body.get(line++).contains("Content-Type: multipart/mixed; boundary=changeset_"));
|
||||
assertEquals(CRLF, body.get(line++));
|
||||
assertTrue(body.get(line++).contains("--changeset_"));
|
||||
assertEquals("Content-Type: application/http" + CRLF, body.get(line++));
|
||||
assertEquals("Content-Transfer-Encoding: binary" + CRLF, body.get(line++));
|
||||
assertEquals("Content-Id: 1" + CRLF, body.get(line++));
|
||||
assertEquals(CRLF, body.get(line++));
|
||||
assertEquals("HTTP/1.1 204 No Content" + CRLF, body.get(line++));
|
||||
assertEquals("Content-Length: 0" + CRLF, body.get(line++));
|
||||
assertEquals(CRLF, body.get(line++));
|
||||
assertEquals(CRLF, body.get(line++));
|
||||
assertTrue(body.get(line++).contains("--changeset_"));
|
||||
assertEquals(CRLF, body.get(line++));
|
||||
assertTrue(body.get(line++).contains("--batch_"));
|
||||
}
|
||||
}
|
2422
lib/server-core/src/test/resources/batchLarge.batch
Normal file
2422
lib/server-core/src/test/resources/batchLarge.batch
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
39
lib/server-core/src/test/resources/batchWithPost.batch
Normal file
39
lib/server-core/src/test/resources/batchWithPost.batch
Normal file
@ -0,0 +1,39 @@
|
||||
--batch_8194-cf13-1f56
|
||||
Content-Type: application/http
|
||||
Content-Transfer-Encoding: binary
|
||||
|
||||
GET http://localhost/odata/Employees('2')/EmployeeName?$format=json HTTP/1.1
|
||||
Accept: application/atomsvc+xml;q=0.8, application/json;odata=verbose;q=0.5, */*;q=0.1
|
||||
Accept-Language:en-US,en;q=0.7,en-UK;q=0.9
|
||||
MaxDataServiceVersion: 2.0
|
||||
|
||||
|
||||
--batch_8194-cf13-1f56
|
||||
Content-Type: multipart/mixed; boundary=changeset_f980-1cb6-94dd
|
||||
|
||||
--changeset_f980-1cb6-94dd
|
||||
Content-Type: application/http
|
||||
Content-Transfer-Encoding: binary
|
||||
Content-ID: changeRequest1
|
||||
|
||||
PUT Employees('2')/EmployeeName HTTP/1.1
|
||||
Content-Length: 100000
|
||||
Accept: application/atomsvc+xml;q=0.8, application/json;odata=verbose;q=0.5, */*;q=0.1
|
||||
DataServiceVersion: 1.0
|
||||
Content-Type: application/json;odata=verbose
|
||||
MaxDataServiceVersion: 2.0
|
||||
|
||||
{"EmployeeName":"Frederic Fall MODIFIED"}
|
||||
|
||||
--changeset_f980-1cb6-94dd--
|
||||
|
||||
--batch_8194-cf13-1f56
|
||||
Content-Type: application/http
|
||||
Content-Transfer-Encoding: binary
|
||||
|
||||
GET Employees('2')/EmployeeName?$format=json HTTP/1.1
|
||||
Accept: application/atomsvc+xml;q=0.8, application/json;odata=verbose;q=0.5, */*;q=0.1
|
||||
MaxDataServiceVersion: 2.0
|
||||
|
||||
|
||||
--batch_8194-cf13-1f56--
|
Loading…
x
Reference in New Issue
Block a user