Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import tools.jackson.core.exc.StreamWriteException;
import tools.jackson.core.io.IOContext;
import tools.jackson.core.json.DupDetector;
import tools.jackson.core.util.ByteArrayBuilder;
import tools.jackson.core.util.SimpleStreamWriteContext;
import tools.jackson.dataformat.xml.XmlPrettyPrinter;
import tools.jackson.dataformat.xml.XmlWriteFeature;
Expand Down Expand Up @@ -1163,6 +1164,16 @@ public int writeBinary(Base64Variant b64variant, InputStream data, int dataLengt
writeNull();
return 0;
}
// A negative length means "length not known, read to the end of stream" per the
// JsonGenerator.writeBinary(...) contract (and as the JSON backend does). Stax2
// needs the full buffer for attribute/typed writes anyway, so read it all and
// reuse the byte[] path; otherwise the negative length reaches a byte[] allocation
// or bounded read and escapes as a raw NegativeArraySizeException / IndexOutOfBoundsException.
if (dataLength < 0) {
byte[] full = _readAll(data);
writeBinary(b64variant, full, 0, full.length);
return full.length;
}
_verifyValueWrite("write Binary value");
if (_nextName == null) {
handleMissingName();
Expand Down Expand Up @@ -1237,7 +1248,24 @@ private byte[] toFullBuffer(byte[] data, int offset, int len)
return result;
}

private byte[] toFullBuffer(InputStream data, final int len) throws JacksonException
private byte[] _readAll(InputStream data) throws JacksonException
{
ByteArrayBuilder bb = new ByteArrayBuilder();
byte[] tmp = new byte[4000];
int count;
try {
while ((count = data.read(tmp)) != -1) {
if (count > 0) {
bb.write(tmp, 0, count);
}
}
} catch (IOException e) {
throw _wrapIOFailure(e);
}
return bb.toByteArray();
}

private byte[] toFullBuffer(InputStream data, final int len) throws JacksonException
{
byte[] result = new byte[len];
int offset = 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package tools.jackson.dataformat.xml.stream;

import java.io.ByteArrayInputStream;
import java.io.StringWriter;

import javax.xml.namespace.QName;

import org.junit.jupiter.api.Test;

import tools.jackson.core.Base64Variants;

import tools.jackson.dataformat.xml.XmlMapper;
import tools.jackson.dataformat.xml.XmlTestUtil;
import tools.jackson.dataformat.xml.ser.ToXmlGenerator;

import static org.junit.jupiter.api.Assertions.assertEquals;

// [dataformat-xml] `JsonGenerator.writeBinary(InputStream, dataLength)` documents a
// negative `dataLength` as "length unknown, read to end" (the JSON backend honors it);
// verify the XML backend streams to end instead of failing with a raw runtime exception.
public class BinaryUnknownLengthWriteTest extends XmlTestUtil
{
private final XmlMapper MAPPER = newMapper();

private final byte[] DATA = "hello, binary world".getBytes();
// base64 of DATA
private final String ENCODED = "aGVsbG8sIGJpbmFyeSB3b3JsZA==";

@Test
public void testElementUnknownLength() throws Exception
{
StringWriter out = new StringWriter();
try (ToXmlGenerator gen = (ToXmlGenerator) MAPPER.createGenerator(out)) {
gen.setNextName(new QName("root"));
gen.writeStartObject();
gen.writeName("bin");
gen.writeBinary(Base64Variants.MIME, new ByteArrayInputStream(DATA), -1);
gen.writeEndObject();
}
String xml = removeSjsxpNamespace(out.toString());
assertEquals("<root><bin>" + ENCODED + "</bin></root>", xml);
}

@Test
public void testAttributeUnknownLength() throws Exception
{
StringWriter out = new StringWriter();
try (ToXmlGenerator gen = (ToXmlGenerator) MAPPER.createGenerator(out)) {
gen.setNextName(new QName("root"));
gen.writeStartObject();
gen.setNextIsAttribute(true);
gen.writeName("bin");
gen.writeBinary(Base64Variants.MIME, new ByteArrayInputStream(DATA), -1);
gen.writeEndObject();
}
String xml = removeSjsxpNamespace(out.toString());
assertEquals("<root bin=\"" + ENCODED + "\"/>", xml);
}
}
Loading