Skip to content

Commit f6022f7

Browse files
committed
multipart as inputstream
1 parent e59680b commit f6022f7

File tree

6 files changed

+173
-7
lines changed

6 files changed

+173
-7
lines changed

client/src/main/java/com/walmartlabs/concord/client/impl/ByteArrayBuffer.java

+20-4
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ public ByteArrayBuffer(int capacity) {
3030
this.array = new byte[capacity];
3131
}
3232

33-
public void append(final byte[] b, final int off, final int len) {
33+
public void append(byte[] b) {
34+
append(b, 0, b.length);
35+
}
36+
37+
public void append(byte[] b, int off, int len) {
3438
if (b == null) {
3539
return;
3640
}
@@ -41,16 +45,16 @@ public void append(final byte[] b, final int off, final int len) {
4145
if (len == 0) {
4246
return;
4347
}
44-
final int newlen = this.len + len;
48+
int newlen = this.len + len;
4549
if (newlen > this.array.length) {
4650
expand(newlen);
4751
}
4852
System.arraycopy(b, off, this.array, this.len, len);
4953
this.len = newlen;
5054
}
5155

52-
private void expand(final int newlen) {
53-
final byte[] newArray = new byte[Math.max(this.array.length << 1, newlen)];
56+
private void expand(int newlen) {
57+
byte[] newArray = new byte[Math.max(this.array.length << 1, newlen)];
5458
System.arraycopy(this.array, 0, newArray, 0, this.len);
5559
this.array = newArray;
5660
}
@@ -59,7 +63,19 @@ public byte[] array() {
5963
return this.array;
6064
}
6165

66+
public byte[] toByteArray() {
67+
final byte[] b = new byte[this.len];
68+
if (this.len > 0) {
69+
System.arraycopy(this.array, 0, b, 0, this.len);
70+
}
71+
return b;
72+
}
73+
6274
public int length() {
6375
return this.len;
6476
}
77+
78+
public void clear() {
79+
this.len = 0;
80+
}
6581
}

client/src/main/java/com/walmartlabs/concord/client/impl/HttpEntity.java

+3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
*/
2222

2323
import java.io.IOException;
24+
import java.io.InputStream;
2425
import java.io.OutputStream;
2526

2627
public interface HttpEntity {
@@ -30,4 +31,6 @@ public interface HttpEntity {
3031
long contentLength() throws IOException;
3132

3233
void writeTo(OutputStream out) throws IOException;
34+
35+
InputStream getContent() throws IOException;
3336
}

client/src/main/java/com/walmartlabs/concord/client/impl/MultipartBuilder.java

+110-3
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@
2020
* =====
2121
*/
2222

23-
import java.io.IOException;
24-
import java.io.OutputStream;
23+
import java.io.*;
2524
import java.nio.ByteBuffer;
2625
import java.nio.CharBuffer;
2726
import java.nio.charset.Charset;
@@ -133,10 +132,60 @@ public ContentType contentType() {
133132
}
134133

135134
@Override
136-
public long contentLength() throws IOException {
135+
public long contentLength() {
137136
return -1;
138137
}
139138

139+
@Override
140+
public InputStream getContent() throws IOException {
141+
SequenceInputStreamBuilder result = new SequenceInputStreamBuilder();
142+
try {
143+
write(result);
144+
return result.build();
145+
} catch (Exception e) {
146+
result.close();
147+
throw e;
148+
}
149+
}
150+
151+
private void write(SequenceInputStreamBuilder result) throws IOException {
152+
ByteArrayBuffer boundaryEncoded = encode(StandardCharsets.US_ASCII, this.boundary);
153+
154+
for (int p = 0, partCount = partHeaders.size(); p < partCount; p++) {
155+
Headers headers = partHeaders.get(p);
156+
RequestBody body = partBodies.get(p);
157+
158+
result.write(DASHDASH);
159+
result.write(boundaryEncoded);
160+
result.write(CRLF);
161+
162+
if (headers != null) {
163+
for (int h = 0, headerCount = headers.size(); h < headerCount; h++) {
164+
writeHeader(headers.name(h), headers.value(h), result);
165+
}
166+
}
167+
168+
ContentType contentType = body.contentType();
169+
if (contentType != null) {
170+
writeHeader("Content-Type", contentType.toString(), result);
171+
}
172+
173+
long contentLength = body.contentLength();
174+
if (contentLength != -1) {
175+
writeHeader("Content-Length", String.valueOf(contentLength), result);
176+
}
177+
178+
result.write(CRLF);
179+
result.write(body.getContent());
180+
result.write(CRLF);
181+
}
182+
183+
result.write(DASHDASH);
184+
result.write(boundaryEncoded);
185+
result.write(DASHDASH);
186+
result.write(CRLF);
187+
}
188+
140189
@Override
141190
public void writeTo(OutputStream out) throws IOException {
142191
ByteArrayBuffer boundaryEncoded = encode(StandardCharsets.US_ASCII, this.boundary);
@@ -178,6 +227,13 @@ public void writeTo(OutputStream out) throws IOException {
178227
out.write(CRLF);
179228
}
180229

230+
private void writeHeader(String name, String value, SequenceInputStreamBuilder out) throws IOException {
231+
out.write(encodeHeader(name));
232+
out.write(COLONSPACE);
233+
out.write(encodeHeader(value));
234+
out.write(CRLF);
235+
}
236+
181237
private void writeHeader(String name, String value, OutputStream out) throws IOException {
182238
write(encodeHeader(name), out);
183239
out.write(COLONSPACE);
@@ -200,4 +256,55 @@ private static void write(ByteArrayBuffer buff, OutputStream out) throws IOExcep
200256
out.write(buff.array(), 0, buff.length());
201257
}
202258
}
259+
260+
static class SequenceInputStreamBuilder {
261+
262+
private final Vector<InputStream> streams = new Vector<>();
263+
private final ByteArrayBuffer currentBuffer = new ByteArrayBuffer(1024);
264+
265+
public void write(byte[] buff) {
266+
currentBuffer.append(buff);
267+
}
268+
269+
public void write(ByteArrayBuffer buff) {
270+
currentBuffer.append(buff.array(), 0, buff.length());
271+
}
272+
273+
public void write(InputStream stream) {
274+
flushCurrentBuffer();
275+
276+
streams.add(stream);
277+
}
278+
279+
public void close() throws IOException {
280+
IOException ioe = null;
281+
for (InputStream in : streams) {
282+
try {
283+
in.close();
284+
} catch (IOException e) {
285+
if (ioe == null) {
286+
ioe = e;
287+
} else {
288+
ioe.addSuppressed(e);
289+
}
290+
}
291+
}
292+
if (ioe != null) {
293+
throw ioe;
294+
}
295+
}
296+
297+
public InputStream build() {
298+
flushCurrentBuffer();
299+
300+
return new SequenceInputStream(streams.elements());
301+
}
302+
303+
private void flushCurrentBuffer() {
304+
if (currentBuffer.length() > 0) {
305+
streams.add(new ByteArrayInputStream(currentBuffer.toByteArray(), 0, currentBuffer.length()));
306+
currentBuffer.clear();
307+
}
308+
}
309+
}
203310
}

client/src/main/java/com/walmartlabs/concord/client/impl/MultipartRequestBodyHandler.java

+10
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ public long contentLength() {
9595
return -1;
9696
}
9797

98+
@Override
99+
public InputStream getContent() {
100+
return in;
101+
}
102+
98103
@Override
99104
public void writeTo(OutputStream out) throws IOException {
100105
try {
@@ -128,6 +133,11 @@ public long contentLength() throws IOException {
128133
return Files.size(path);
129134
}
130135

136+
@Override
137+
public InputStream getContent() throws IOException {
138+
return Files.newInputStream(this.path);
139+
}
140+
131141
@Override
132142
public void writeTo(OutputStream out) throws IOException {
133143
try (InputStream in = Files.newInputStream(this.path)) {

client/src/main/java/com/walmartlabs/concord/client/impl/RequestBody.java

+7
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
* =====
2121
*/
2222

23+
import java.io.ByteArrayInputStream;
2324
import java.io.IOException;
25+
import java.io.InputStream;
2426
import java.io.OutputStream;
2527
import java.nio.charset.Charset;
2628
import java.nio.charset.StandardCharsets;
@@ -63,6 +65,11 @@ public long contentLength() {
6365
public void writeTo(OutputStream out) throws IOException {
6466
out.write(content, offset, byteCount);
6567
}
68+
69+
@Override
70+
public InputStream getContent() {
71+
return new ByteArrayInputStream(content, offset, byteCount);
72+
}
6673
};
6774
}
6875
}

client/src/test/java/com/walmartlabs/concord/client/impl/MultipartRequestBodyHandlerTest.java

+23
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
import java.io.ByteArrayInputStream;
2727
import java.io.ByteArrayOutputStream;
28+
import java.io.InputStream;
2829
import java.nio.charset.StandardCharsets;
2930
import java.util.Collections;
3031
import java.util.LinkedHashMap;
@@ -58,6 +59,28 @@ public void test() throws Exception {
5859
assertEquals("multipart/form-data; boundary=e572b648-941a-4648-97ed-0e3c5350f0ad", entity.contentType().toString());
5960
}
6061

62+
@Test
63+
public void test2() throws Exception {
64+
Map<String, Object> data = new LinkedHashMap<>();
65+
data.put("string-field", "this stuff");
66+
data.put("byte[]-field", "byte array".getBytes());
67+
data.put("inputstream-field", new ByteArrayInputStream("my input stream".getBytes()));
68+
data.put("boolean-field", true);
69+
data.put("json-field", Collections.singletonMap("k", "v"));
70+
data.put("string[]-field", new String[] {"one", "two"});
71+
data.put("uuid-field", UUID.fromString("f8d30c37-4c84-4817-9cb8-23b27a54c459"));
72+
73+
MultipartBuilder mpb = new MultipartBuilder("e572b648-941a-4648-97ed-0e3c5350f0ad");
74+
HttpEntity entity = MultipartRequestBodyHandler.handle(mpb, new ObjectMapper(), data);
75+
76+
try (InputStream is = entity.getContent()) {
77+
String str = new String(is.readAllBytes(), StandardCharsets.UTF_8);
78+
79+
assertEquals(body, str);
80+
assertEquals("multipart/form-data; boundary=e572b648-941a-4648-97ed-0e3c5350f0ad", entity.contentType().toString());
81+
}
82+
}
83+
6184
private static final String body = "--e572b648-941a-4648-97ed-0e3c5350f0ad\r\n" +
6285
"Content-Disposition: form-data; name=\"string-field\"\r\n" +
6386
"Content-Length: 10\r\n" +

0 commit comments

Comments
 (0)