From: Jakub Morvay Date: Tue, 12 Sep 2017 14:48:23 +0000 (+0200) Subject: Bug 8988 - Check for empty payload properly X-Git-Tag: release/oxygen~69^2 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=0f9babadeecb83013782d8044c27de2f1998f97b;p=netconf.git Bug 8988 - Check for empty payload properly Do not use InputStream#avalaible method to check for empty payload's input streams in our implementations of MessageBodyReader interface. InputStream#avalaible method returns an estimate of the number of bytes that can be read from input stream without blocking. We cannot be sure the stream is actually empty, even if this method returns 0. Wrap the payload's input stream in PushbackInputStream and try to read and unread from it instead. Change-Id: I43b3a8d837f3dc4bc59d7cc2db29ffa06786844f Signed-off-by: Jakub Morvay --- diff --git a/restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/util/RestUtil.java b/restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/util/RestUtil.java index 9deb2ca032..ff04373f2c 100644 --- a/restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/util/RestUtil.java +++ b/restconf/restconf-common/src/main/java/org/opendaylight/restconf/common/util/RestUtil.java @@ -7,8 +7,13 @@ */ package org.opendaylight.restconf.common.util; +import com.google.common.base.Preconditions; +import java.io.IOException; +import java.io.InputStream; +import java.io.PushbackInputStream; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.xml.stream.events.StartElement; @@ -32,6 +37,30 @@ public final class RestUtil { return superType; } + /** + * Utility method to find out if is provided {@link InputStream} empty. + * + * @param entityStream {@link InputStream} to be checked if it is empty. + * @return Empty Optional if provided input stream is empty or Optional + * containing input stream otherwise. + * + * @throws IOException if an IO error arises during stream inspection. + * @throws NullPointerException if provided stream is null. + * + */ + public static Optional isInputStreamEmpty(final InputStream entityStream) throws IOException { + Preconditions.checkNotNull(entityStream); + final PushbackInputStream pushbackInputStream = new PushbackInputStream(entityStream); + + int firstByte = pushbackInputStream.read(); + if (firstByte == -1) { + return Optional.empty(); + } else { + pushbackInputStream.unread(firstByte); + return Optional.of(pushbackInputStream); + } + } + public static IdentityValuesDTO asInstanceIdentifier(final String value, final PrefixesMaping prefixMap) { final String valueTrimmed = value.trim(); if (!valueTrimmed.startsWith("/")) { diff --git a/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/JsonNormalizedNodeBodyReader.java b/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/JsonNormalizedNodeBodyReader.java index f02de61459..4ce6997aa0 100644 --- a/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/JsonNormalizedNodeBodyReader.java +++ b/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/JsonNormalizedNodeBodyReader.java @@ -16,6 +16,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import javax.ws.rs.Consumes; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; @@ -30,6 +31,7 @@ import org.opendaylight.restconf.common.context.NormalizedNodeContext; import org.opendaylight.restconf.common.errors.RestconfDocumentedException; import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag; import org.opendaylight.restconf.common.errors.RestconfError.ErrorType; +import org.opendaylight.restconf.common.util.RestUtil; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode; import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode; @@ -92,7 +94,8 @@ public class JsonNormalizedNodeBodyReader private static NormalizedNodeContext readFrom(final InstanceIdentifierContext path, final InputStream entityStream, final boolean isPost) throws IOException { - if (entityStream.available() < 1) { + final Optional nonEmptyInputStreamOptional = RestUtil.isInputStreamEmpty(entityStream); + if (!nonEmptyInputStreamOptional.isPresent()) { return new NormalizedNodeContext(path, null); } final NormalizedNodeResult resultHolder = new NormalizedNodeResult(); @@ -114,7 +117,7 @@ public class JsonNormalizedNodeBodyReader } final JsonParserStream jsonParser = JsonParserStream.create(writer, path.getSchemaContext(), parentSchema); - final JsonReader reader = new JsonReader(new InputStreamReader(entityStream)); + final JsonReader reader = new JsonReader(new InputStreamReader(nonEmptyInputStreamOptional.get())); jsonParser.parse(reader); NormalizedNode result = resultHolder.getResult(); diff --git a/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/JsonToPatchBodyReader.java b/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/JsonToPatchBodyReader.java index da36eefeb1..d902f3648b 100644 --- a/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/JsonToPatchBodyReader.java +++ b/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/JsonToPatchBodyReader.java @@ -19,6 +19,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import javax.annotation.Nonnull; import javax.ws.rs.Consumes; import javax.ws.rs.WebApplicationException; @@ -36,6 +37,7 @@ import org.opendaylight.restconf.common.errors.RestconfError.ErrorType; import org.opendaylight.restconf.common.patch.PatchContext; import org.opendaylight.restconf.common.patch.PatchEditOperation; import org.opendaylight.restconf.common.patch.PatchEntity; +import org.opendaylight.restconf.common.util.RestUtil; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; @@ -95,11 +97,12 @@ public class JsonToPatchBodyReader extends AbstractIdentifierAwareJaxRsProvider private PatchContext readFrom(final InstanceIdentifierContext path, final InputStream entityStream) throws IOException { - if (entityStream.available() < 1) { + final Optional nonEmptyInputStreamOptional = RestUtil.isInputStreamEmpty(entityStream); + if (!nonEmptyInputStreamOptional.isPresent()) { return new PatchContext(path, null, null); } - final JsonReader jsonReader = new JsonReader(new InputStreamReader(entityStream)); + final JsonReader jsonReader = new JsonReader(new InputStreamReader(nonEmptyInputStreamOptional.get())); final List resultList = read(jsonReader, path); jsonReader.close(); diff --git a/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/XmlNormalizedNodeBodyReader.java b/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/XmlNormalizedNodeBodyReader.java index e4f114f0ea..4c2eae4992 100644 --- a/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/XmlNormalizedNodeBodyReader.java +++ b/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/XmlNormalizedNodeBodyReader.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Deque; import java.util.List; +import java.util.Optional; import javax.ws.rs.Consumes; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; @@ -35,6 +36,7 @@ import org.opendaylight.restconf.common.context.NormalizedNodeContext; import org.opendaylight.restconf.common.errors.RestconfDocumentedException; import org.opendaylight.restconf.common.errors.RestconfError.ErrorTag; import org.opendaylight.restconf.common.errors.RestconfError.ErrorType; +import org.opendaylight.restconf.common.util.RestUtil; import org.opendaylight.yangtools.util.xml.UntrustedXML; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; @@ -96,13 +98,13 @@ public class XmlNormalizedNodeBodyReader extends AbstractIdentifierAwareJaxRsPro private NormalizedNodeContext readFrom(final InputStream entityStream) throws IOException, SAXException, XMLStreamException, ParserConfigurationException, URISyntaxException { final InstanceIdentifierContext path = getInstanceIdentifierContext(); - - if (entityStream.available() < 1) { + final Optional nonEmptyInputStreamOptional = RestUtil.isInputStreamEmpty(entityStream); + if (!nonEmptyInputStreamOptional.isPresent()) { // represent empty nopayload input return new NormalizedNodeContext(path, null); } - final Document doc = UntrustedXML.newDocumentBuilder().parse(entityStream); + final Document doc = UntrustedXML.newDocumentBuilder().parse(nonEmptyInputStreamOptional.get()); return parse(path, doc); } diff --git a/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/XmlToPatchBodyReader.java b/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/XmlToPatchBodyReader.java index 56e3733fa0..c81c770e8c 100644 --- a/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/XmlToPatchBodyReader.java +++ b/restconf/restconf-nb-bierman02/src/main/java/org/opendaylight/netconf/sal/rest/impl/XmlToPatchBodyReader.java @@ -19,6 +19,7 @@ import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.Optional; import javax.annotation.Nonnull; import javax.ws.rs.Consumes; import javax.ws.rs.WebApplicationException; @@ -38,6 +39,7 @@ import org.opendaylight.restconf.common.errors.RestconfError.ErrorType; import org.opendaylight.restconf.common.patch.PatchContext; import org.opendaylight.restconf.common.patch.PatchEditOperation; import org.opendaylight.restconf.common.patch.PatchEntity; +import org.opendaylight.restconf.common.util.RestUtil; import org.opendaylight.yangtools.util.xml.UntrustedXML; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; @@ -90,13 +92,13 @@ public class XmlToPatchBodyReader extends AbstractIdentifierAwareJaxRsProvider i try { final InstanceIdentifierContext path = getInstanceIdentifierContext(); - - if (entityStream.available() < 1) { + final Optional nonEmptyInputStreamOptional = RestUtil.isInputStreamEmpty(entityStream); + if (!nonEmptyInputStreamOptional.isPresent()) { // represent empty nopayload input return new PatchContext(path, null, null); } - final Document doc = UntrustedXML.newDocumentBuilder().parse(entityStream); + final Document doc = UntrustedXML.newDocumentBuilder().parse(nonEmptyInputStreamOptional.get()); return parse(path, doc); } catch (final RestconfDocumentedException e) { throw e; diff --git a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/spi/AbstractIdentifierAwareJaxRsProvider.java b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/spi/AbstractIdentifierAwareJaxRsProvider.java index ac87d2a1e8..ab40b3274a 100644 --- a/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/spi/AbstractIdentifierAwareJaxRsProvider.java +++ b/restconf/restconf-nb-rfc8040/src/main/java/org/opendaylight/restconf/nb/rfc8040/jersey/providers/spi/AbstractIdentifierAwareJaxRsProvider.java @@ -11,6 +11,7 @@ package org.opendaylight.restconf.nb.rfc8040.jersey.providers.spi; import com.google.common.base.Optional; import java.io.IOException; import java.io.InputStream; +import java.io.PushbackInputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import javax.ws.rs.HttpMethod; @@ -47,11 +48,17 @@ public abstract class AbstractIdentifierAwareJaxRsProvider implements Message final MultivaluedMap httpHeaders, final InputStream entityStream) throws IOException, WebApplicationException { final InstanceIdentifierContext path = getInstanceIdentifierContext(); - if (entityStream.available() < 1) { + + final PushbackInputStream pushbackInputStream = new PushbackInputStream(entityStream); + + int firstByte = pushbackInputStream.read(); + if (firstByte == -1) { return emptyBody(path); + } else { + pushbackInputStream.unread(firstByte); + return readBody(path, pushbackInputStream); } - return readBody(path, entityStream); } /**