Refactor to enable support for non-form based multipart requests
This commit is contained in:
parent
91f9278b9a
commit
d71d0f5a4c
|
@ -46,7 +46,7 @@ import org.apache.hc.core5.util.ByteArrayBuffer;
|
||||||
*
|
*
|
||||||
* @since 4.3
|
* @since 4.3
|
||||||
*/
|
*/
|
||||||
abstract class AbstractMultipartForm {
|
abstract class AbstractMultipartFormat {
|
||||||
|
|
||||||
static ByteArrayBuffer encode(
|
static ByteArrayBuffer encode(
|
||||||
final Charset charset, final String string) {
|
final Charset charset, final String string) {
|
||||||
|
@ -103,25 +103,25 @@ abstract class AbstractMultipartForm {
|
||||||
* @param boundary to use - must not be {@code null}
|
* @param boundary to use - must not be {@code null}
|
||||||
* @throws IllegalArgumentException if charset is null or boundary is null
|
* @throws IllegalArgumentException if charset is null or boundary is null
|
||||||
*/
|
*/
|
||||||
public AbstractMultipartForm(final Charset charset, final String boundary) {
|
public AbstractMultipartFormat(final Charset charset, final String boundary) {
|
||||||
super();
|
super();
|
||||||
Args.notNull(boundary, "Multipart boundary");
|
Args.notNull(boundary, "Multipart boundary");
|
||||||
this.charset = charset != null ? charset : StandardCharsets.ISO_8859_1;
|
this.charset = charset != null ? charset : StandardCharsets.ISO_8859_1;
|
||||||
this.boundary = boundary;
|
this.boundary = boundary;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AbstractMultipartForm(final String boundary) {
|
public AbstractMultipartFormat(final String boundary) {
|
||||||
this(null, boundary);
|
this(null, boundary);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract List<FormBodyPart> getBodyParts();
|
public abstract List<MultipartPart> getParts();
|
||||||
|
|
||||||
void doWriteTo(
|
void doWriteTo(
|
||||||
final OutputStream out,
|
final OutputStream out,
|
||||||
final boolean writeContent) throws IOException {
|
final boolean writeContent) throws IOException {
|
||||||
|
|
||||||
final ByteArrayBuffer boundaryEncoded = encode(this.charset, this.boundary);
|
final ByteArrayBuffer boundaryEncoded = encode(this.charset, this.boundary);
|
||||||
for (final FormBodyPart part: getBodyParts()) {
|
for (final MultipartPart part: getParts()) {
|
||||||
writeBytes(TWO_DASHES, out);
|
writeBytes(TWO_DASHES, out);
|
||||||
writeBytes(boundaryEncoded, out);
|
writeBytes(boundaryEncoded, out);
|
||||||
writeBytes(CR_LF, out);
|
writeBytes(CR_LF, out);
|
||||||
|
@ -145,7 +145,7 @@ abstract class AbstractMultipartForm {
|
||||||
* Write the multipart header fields; depends on the style.
|
* Write the multipart header fields; depends on the style.
|
||||||
*/
|
*/
|
||||||
protected abstract void formatMultipartHeader(
|
protected abstract void formatMultipartHeader(
|
||||||
final FormBodyPart part,
|
final MultipartPart part,
|
||||||
final OutputStream out) throws IOException;
|
final OutputStream out) throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -173,7 +173,7 @@ abstract class AbstractMultipartForm {
|
||||||
*/
|
*/
|
||||||
public long getTotalLength() {
|
public long getTotalLength() {
|
||||||
long contentLen = 0;
|
long contentLen = 0;
|
||||||
for (final FormBodyPart part: getBodyParts()) {
|
for (final MultipartPart part: getParts()) {
|
||||||
final ContentBody body = part.getBody();
|
final ContentBody body = part.getBody();
|
||||||
final long len = body.getContentLength();
|
final long len = body.getContentLength();
|
||||||
if (len >= 0) {
|
if (len >= 0) {
|
|
@ -36,36 +36,24 @@ import org.apache.hc.core5.util.Args;
|
||||||
*
|
*
|
||||||
* @since 4.0
|
* @since 4.0
|
||||||
*/
|
*/
|
||||||
public class FormBodyPart {
|
public class FormBodyPart extends MultipartPart {
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
private final Header header;
|
|
||||||
private final ContentBody body;
|
|
||||||
|
|
||||||
FormBodyPart(final String name, final ContentBody body, final Header header) {
|
FormBodyPart(final String name, final ContentBody body, final Header header) {
|
||||||
super();
|
super(body, header);
|
||||||
Args.notNull(name, "Name");
|
Args.notNull(name, "Name");
|
||||||
Args.notNull(body, "Body");
|
Args.notNull(body, "Body");
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.body = body;
|
|
||||||
this.header = header != null ? header : new Header();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ContentBody getBody() {
|
|
||||||
return this.body;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Header getHeader() {
|
|
||||||
return this.header;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addField(final String name, final String value) {
|
public void addField(final String name, final String value) {
|
||||||
Args.notNull(name, "Field name");
|
Args.notNull(name, "Field name");
|
||||||
this.header.addField(new MinimalField(name, value));
|
super.addField(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,20 +38,20 @@ import java.util.List;
|
||||||
*
|
*
|
||||||
* @since 4.3
|
* @since 4.3
|
||||||
*/
|
*/
|
||||||
class HttpBrowserCompatibleMultipart extends AbstractMultipartForm {
|
class HttpBrowserCompatibleMultipart extends AbstractMultipartFormat {
|
||||||
|
|
||||||
private final List<FormBodyPart> parts;
|
private final List<MultipartPart> parts;
|
||||||
|
|
||||||
public HttpBrowserCompatibleMultipart(
|
public HttpBrowserCompatibleMultipart(
|
||||||
final Charset charset,
|
final Charset charset,
|
||||||
final String boundary,
|
final String boundary,
|
||||||
final List<FormBodyPart> parts) {
|
final List<MultipartPart> parts) {
|
||||||
super(charset, boundary);
|
super(charset, boundary);
|
||||||
this.parts = parts;
|
this.parts = parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<FormBodyPart> getBodyParts() {
|
public List<MultipartPart> getParts() {
|
||||||
return this.parts;
|
return this.parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,13 +60,15 @@ class HttpBrowserCompatibleMultipart extends AbstractMultipartForm {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void formatMultipartHeader(
|
protected void formatMultipartHeader(
|
||||||
final FormBodyPart part,
|
final MultipartPart part,
|
||||||
final OutputStream out) throws IOException {
|
final OutputStream out) throws IOException {
|
||||||
// For browser-compatible, only write Content-Disposition
|
// For browser-compatible, only write Content-Disposition
|
||||||
// Use content charset
|
// Use content charset
|
||||||
final Header header = part.getHeader();
|
final Header header = part.getHeader();
|
||||||
final MinimalField cd = header.getField(MIME.CONTENT_DISPOSITION);
|
final MinimalField cd = header.getField(MIME.CONTENT_DISPOSITION);
|
||||||
writeField(cd, this.charset, out);
|
if (cd != null) {
|
||||||
|
writeField(cd, this.charset, out);
|
||||||
|
}
|
||||||
final String filename = part.getBody().getFilename();
|
final String filename = part.getBody().getFilename();
|
||||||
if (filename != null) {
|
if (filename != null) {
|
||||||
final MinimalField ct = header.getField(MIME.CONTENT_TYPE);
|
final MinimalField ct = header.getField(MIME.CONTENT_TYPE);
|
||||||
|
|
|
@ -40,26 +40,26 @@ import java.util.List;
|
||||||
*
|
*
|
||||||
* @since 4.3
|
* @since 4.3
|
||||||
*/
|
*/
|
||||||
class HttpRFC6532Multipart extends AbstractMultipartForm {
|
class HttpRFC6532Multipart extends AbstractMultipartFormat {
|
||||||
|
|
||||||
private final List<FormBodyPart> parts;
|
private final List<MultipartPart> parts;
|
||||||
|
|
||||||
public HttpRFC6532Multipart(
|
public HttpRFC6532Multipart(
|
||||||
final Charset charset,
|
final Charset charset,
|
||||||
final String boundary,
|
final String boundary,
|
||||||
final List<FormBodyPart> parts) {
|
final List<MultipartPart> parts) {
|
||||||
super(charset, boundary);
|
super(charset, boundary);
|
||||||
this.parts = parts;
|
this.parts = parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<FormBodyPart> getBodyParts() {
|
public List<MultipartPart> getParts() {
|
||||||
return this.parts;
|
return this.parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void formatMultipartHeader(
|
protected void formatMultipartHeader(
|
||||||
final FormBodyPart part,
|
final MultipartPart part,
|
||||||
final OutputStream out) throws IOException {
|
final OutputStream out) throws IOException {
|
||||||
|
|
||||||
// For RFC6532, we output all fields with UTF-8 encoding.
|
// For RFC6532, we output all fields with UTF-8 encoding.
|
||||||
|
|
|
@ -40,27 +40,27 @@ import org.apache.commons.codec.DecoderException;
|
||||||
import org.apache.hc.core5.http.NameValuePair;
|
import org.apache.hc.core5.http.NameValuePair;
|
||||||
import org.apache.hc.core5.util.ByteArrayBuffer;
|
import org.apache.hc.core5.util.ByteArrayBuffer;
|
||||||
|
|
||||||
public class HttpRFC7578Multipart extends AbstractMultipartForm {
|
public class HttpRFC7578Multipart extends AbstractMultipartFormat {
|
||||||
|
|
||||||
private static final PercentCodec PERCENT_CODEC = new PercentCodec();
|
private static final PercentCodec PERCENT_CODEC = new PercentCodec();
|
||||||
|
|
||||||
private final List<FormBodyPart> parts;
|
private final List<MultipartPart> parts;
|
||||||
|
|
||||||
public HttpRFC7578Multipart(
|
public HttpRFC7578Multipart(
|
||||||
final Charset charset,
|
final Charset charset,
|
||||||
final String boundary,
|
final String boundary,
|
||||||
final List<FormBodyPart> parts) {
|
final List<MultipartPart> parts) {
|
||||||
super(charset, boundary);
|
super(charset, boundary);
|
||||||
this.parts = parts;
|
this.parts = parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<FormBodyPart> getBodyParts() {
|
public List<MultipartPart> getParts() {
|
||||||
return parts;
|
return parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void formatMultipartHeader(final FormBodyPart part, final OutputStream out) throws IOException {
|
protected void formatMultipartHeader(final MultipartPart part, final OutputStream out) throws IOException {
|
||||||
for (final MinimalField field: part.getHeader()) {
|
for (final MinimalField field: part.getHeader()) {
|
||||||
if (MIME.CONTENT_DISPOSITION.equalsIgnoreCase(field.getName())) {
|
if (MIME.CONTENT_DISPOSITION.equalsIgnoreCase(field.getName())) {
|
||||||
writeBytes(field.getName(), charset, out);
|
writeBytes(field.getName(), charset, out);
|
||||||
|
|
|
@ -39,26 +39,26 @@ import java.util.List;
|
||||||
*
|
*
|
||||||
* @since 4.3
|
* @since 4.3
|
||||||
*/
|
*/
|
||||||
class HttpStrictMultipart extends AbstractMultipartForm {
|
class HttpStrictMultipart extends AbstractMultipartFormat {
|
||||||
|
|
||||||
private final List<FormBodyPart> parts;
|
private final List<MultipartPart> parts;
|
||||||
|
|
||||||
public HttpStrictMultipart(
|
public HttpStrictMultipart(
|
||||||
final Charset charset,
|
final Charset charset,
|
||||||
final String boundary,
|
final String boundary,
|
||||||
final List<FormBodyPart> parts) {
|
final List<MultipartPart> parts) {
|
||||||
super(charset, boundary);
|
super(charset, boundary);
|
||||||
this.parts = parts;
|
this.parts = parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<FormBodyPart> getBodyParts() {
|
public List<MultipartPart> getParts() {
|
||||||
return this.parts;
|
return this.parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void formatMultipartHeader(
|
protected void formatMultipartHeader(
|
||||||
final FormBodyPart part,
|
final MultipartPart part,
|
||||||
final OutputStream out) throws IOException {
|
final OutputStream out) throws IOException {
|
||||||
|
|
||||||
// For strict, we output all fields with MIME-standard encoding.
|
// For strict, we output all fields with MIME-standard encoding.
|
||||||
|
|
|
@ -45,7 +45,7 @@ import org.apache.hc.core5.util.Args;
|
||||||
/**
|
/**
|
||||||
* Builder for multipart {@link HttpEntity}s.
|
* Builder for multipart {@link HttpEntity}s.
|
||||||
*
|
*
|
||||||
* @since 4.3
|
* @since 5.0
|
||||||
*/
|
*/
|
||||||
public class MultipartEntityBuilder {
|
public class MultipartEntityBuilder {
|
||||||
|
|
||||||
|
@ -56,13 +56,14 @@ public class MultipartEntityBuilder {
|
||||||
"-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
"-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
.toCharArray();
|
.toCharArray();
|
||||||
|
|
||||||
private final static String DEFAULT_SUBTYPE = "form-data";
|
private final static String FORM_SUBTYPE = "form-data";
|
||||||
|
private final static String MIXED_SUBTYPE = "mixed";
|
||||||
|
|
||||||
private ContentType contentType;
|
private ContentType contentType;
|
||||||
private HttpMultipartMode mode = HttpMultipartMode.STRICT;
|
private HttpMultipartMode mode = HttpMultipartMode.STRICT;
|
||||||
private String boundary = null;
|
private String boundary = null;
|
||||||
private Charset charset = null;
|
private Charset charset = null;
|
||||||
private List<FormBodyPart> bodyParts = null;
|
private List<MultipartPart> multipartParts = null;
|
||||||
|
|
||||||
public static MultipartEntityBuilder create() {
|
public static MultipartEntityBuilder create() {
|
||||||
return new MultipartEntityBuilder();
|
return new MultipartEntityBuilder();
|
||||||
|
@ -117,14 +118,14 @@ public class MultipartEntityBuilder {
|
||||||
/**
|
/**
|
||||||
* @since 4.4
|
* @since 4.4
|
||||||
*/
|
*/
|
||||||
public MultipartEntityBuilder addPart(final FormBodyPart bodyPart) {
|
public MultipartEntityBuilder addPart(final MultipartPart multipartPart) {
|
||||||
if (bodyPart == null) {
|
if (multipartPart == null) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
if (this.bodyParts == null) {
|
if (this.multipartParts == null) {
|
||||||
this.bodyParts = new ArrayList<>();
|
this.multipartParts = new ArrayList<>();
|
||||||
}
|
}
|
||||||
this.bodyParts.add(bodyPart);
|
this.multipartParts.add(multipartPart);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,28 +203,46 @@ public class MultipartEntityBuilder {
|
||||||
paramsList.add(new BasicNameValuePair("charset", charsetCopy.name()));
|
paramsList.add(new BasicNameValuePair("charset", charsetCopy.name()));
|
||||||
}
|
}
|
||||||
final NameValuePair[] params = paramsList.toArray(new NameValuePair[paramsList.size()]);
|
final NameValuePair[] params = paramsList.toArray(new NameValuePair[paramsList.size()]);
|
||||||
final ContentType contentTypeCopy = contentType != null ?
|
|
||||||
contentType.withParameters(params) :
|
final ContentType contentTypeCopy;
|
||||||
ContentType.create("multipart/" + DEFAULT_SUBTYPE, params);
|
if (contentType != null) {
|
||||||
final List<FormBodyPart> bodyPartsCopy = bodyParts != null ? new ArrayList<>(bodyParts) :
|
contentTypeCopy = contentType.withParameters(params);
|
||||||
Collections.<FormBodyPart>emptyList();
|
} else {
|
||||||
|
boolean formData = false;
|
||||||
|
if (multipartParts != null) {
|
||||||
|
for (final MultipartPart multipartPart : multipartParts) {
|
||||||
|
if (multipartPart instanceof FormBodyPart) {
|
||||||
|
formData = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formData) {
|
||||||
|
contentTypeCopy = ContentType.create("multipart/" + FORM_SUBTYPE, params);
|
||||||
|
} else {
|
||||||
|
contentTypeCopy = ContentType.create("multipart/" + MIXED_SUBTYPE, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final List<MultipartPart> multipartPartsCopy = multipartParts != null ? new ArrayList<>(multipartParts) :
|
||||||
|
Collections.<MultipartPart>emptyList();
|
||||||
final HttpMultipartMode modeCopy = mode != null ? mode : HttpMultipartMode.STRICT;
|
final HttpMultipartMode modeCopy = mode != null ? mode : HttpMultipartMode.STRICT;
|
||||||
final AbstractMultipartForm form;
|
final AbstractMultipartFormat form;
|
||||||
switch (modeCopy) {
|
switch (modeCopy) {
|
||||||
case BROWSER_COMPATIBLE:
|
case BROWSER_COMPATIBLE:
|
||||||
form = new HttpBrowserCompatibleMultipart(charsetCopy, boundaryCopy, bodyPartsCopy);
|
form = new HttpBrowserCompatibleMultipart(charsetCopy, boundaryCopy, multipartPartsCopy);
|
||||||
break;
|
break;
|
||||||
case RFC6532:
|
case RFC6532:
|
||||||
form = new HttpRFC6532Multipart(charsetCopy, boundaryCopy, bodyPartsCopy);
|
form = new HttpRFC6532Multipart(charsetCopy, boundaryCopy, multipartPartsCopy);
|
||||||
break;
|
break;
|
||||||
case RFC7578:
|
case RFC7578:
|
||||||
if (charsetCopy == null) {
|
if (charsetCopy == null) {
|
||||||
charsetCopy = StandardCharsets.UTF_8;
|
charsetCopy = StandardCharsets.UTF_8;
|
||||||
}
|
}
|
||||||
form = new HttpRFC7578Multipart(charsetCopy, boundaryCopy, bodyPartsCopy);
|
form = new HttpRFC7578Multipart(charsetCopy, boundaryCopy, multipartPartsCopy);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
form = new HttpStrictMultipart(StandardCharsets.US_ASCII, boundaryCopy, bodyPartsCopy);
|
form = new HttpStrictMultipart(StandardCharsets.US_ASCII, boundaryCopy, multipartPartsCopy);
|
||||||
}
|
}
|
||||||
return new MultipartFormEntity(form, contentTypeCopy, form.getTotalLength());
|
return new MultipartFormEntity(form, contentTypeCopy, form.getTotalLength());
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,12 +43,12 @@ import org.apache.hc.core5.http.HttpEntity;
|
||||||
|
|
||||||
class MultipartFormEntity implements HttpEntity {
|
class MultipartFormEntity implements HttpEntity {
|
||||||
|
|
||||||
private final AbstractMultipartForm multipart;
|
private final AbstractMultipartFormat multipart;
|
||||||
private final ContentType contentType;
|
private final ContentType contentType;
|
||||||
private final long contentLength;
|
private final long contentLength;
|
||||||
|
|
||||||
MultipartFormEntity(
|
MultipartFormEntity(
|
||||||
final AbstractMultipartForm multipart,
|
final AbstractMultipartFormat multipart,
|
||||||
final ContentType contentType,
|
final ContentType contentType,
|
||||||
final long contentLength) {
|
final long contentLength) {
|
||||||
super();
|
super();
|
||||||
|
@ -57,7 +57,7 @@ class MultipartFormEntity implements HttpEntity {
|
||||||
this.contentLength = contentLength;
|
this.contentLength = contentLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
AbstractMultipartForm getMultipart() {
|
AbstractMultipartFormat getMultipart() {
|
||||||
return this.multipart;
|
return this.multipart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* 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.
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many
|
||||||
|
* individuals on behalf of the Apache Software Foundation. For more
|
||||||
|
* information on the Apache Software Foundation, please see
|
||||||
|
* <http://www.apache.org/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.hc.client5.http.entity.mime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MultipartPart class represents a content body that can be used as a part of multipart encoded
|
||||||
|
* entities. This class automatically populates the header with standard fields based on
|
||||||
|
* the content description of the enclosed body.
|
||||||
|
*
|
||||||
|
* @since 5.0
|
||||||
|
*/
|
||||||
|
public class MultipartPart {
|
||||||
|
|
||||||
|
private final Header header;
|
||||||
|
private final ContentBody body;
|
||||||
|
|
||||||
|
MultipartPart(final ContentBody body, final Header header) {
|
||||||
|
super();
|
||||||
|
this.body = body;
|
||||||
|
this.header = header != null ? header : new Header();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContentBody getBody() {
|
||||||
|
return this.body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Header getHeader() {
|
||||||
|
return this.header;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addField(final String name, final String value) {
|
||||||
|
addField(new MinimalField(name, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void addField(final MinimalField field) {
|
||||||
|
this.header.addField(field);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many
|
||||||
|
* individuals on behalf of the Apache Software Foundation. For more
|
||||||
|
* information on the Apache Software Foundation, please see
|
||||||
|
* <http://www.apache.org/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.hc.client5.http.entity.mime;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.hc.core5.http.ContentType;
|
||||||
|
import org.apache.hc.core5.http.NameValuePair;
|
||||||
|
import org.apache.hc.core5.util.Args;
|
||||||
|
import org.apache.hc.core5.util.Asserts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder for individual {@link MultipartPart}s.
|
||||||
|
*
|
||||||
|
* @since 4.4
|
||||||
|
*/
|
||||||
|
public class MultipartPartBuilder {
|
||||||
|
|
||||||
|
private ContentBody body;
|
||||||
|
private final Header header;
|
||||||
|
|
||||||
|
public static MultipartPartBuilder create(final ContentBody body) {
|
||||||
|
return new MultipartPartBuilder(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MultipartPartBuilder create() {
|
||||||
|
return new MultipartPartBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
MultipartPartBuilder(final ContentBody body) {
|
||||||
|
this();
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
MultipartPartBuilder() {
|
||||||
|
this.header = new Header();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultipartPartBuilder setBody(final ContentBody body) {
|
||||||
|
this.body = body;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultipartPartBuilder addHeader(final String name, final String value, final List<NameValuePair> parameters) {
|
||||||
|
Args.notNull(name, "Header name");
|
||||||
|
this.header.addField(new MinimalField(name, value, parameters));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultipartPartBuilder addHeader(final String name, final String value) {
|
||||||
|
Args.notNull(name, "Header name");
|
||||||
|
this.header.addField(new MinimalField(name, value));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultipartPartBuilder setHeader(final String name, final String value) {
|
||||||
|
Args.notNull(name, "Header name");
|
||||||
|
this.header.setField(new MinimalField(name, value));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultipartPartBuilder removeHeaders(final String name) {
|
||||||
|
Args.notNull(name, "Header name");
|
||||||
|
this.header.removeFields(name);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultipartPart build() {
|
||||||
|
Asserts.notNull(this.body, "Content body");
|
||||||
|
final Header headerCopy = new Header();
|
||||||
|
final List<MinimalField> fields = this.header.getFields();
|
||||||
|
for (final MinimalField field: fields) {
|
||||||
|
headerCopy.addField(field);
|
||||||
|
}
|
||||||
|
if (headerCopy.getField(MIME.CONTENT_TYPE) == null) {
|
||||||
|
final ContentType contentType;
|
||||||
|
if (body instanceof AbstractContentBody) {
|
||||||
|
contentType = ((AbstractContentBody) body).getContentType();
|
||||||
|
} else {
|
||||||
|
contentType = null;
|
||||||
|
}
|
||||||
|
if (contentType != null) {
|
||||||
|
headerCopy.addField(new MinimalField(MIME.CONTENT_TYPE, contentType.toString()));
|
||||||
|
} else {
|
||||||
|
final StringBuilder buffer = new StringBuilder();
|
||||||
|
buffer.append(this.body.getMimeType()); // MimeType cannot be null
|
||||||
|
if (this.body.getCharset() != null) { // charset may legitimately be null
|
||||||
|
buffer.append("; charset=");
|
||||||
|
buffer.append(this.body.getCharset());
|
||||||
|
}
|
||||||
|
headerCopy.addField(new MinimalField(MIME.CONTENT_TYPE, buffer.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new MultipartPart(this.body, headerCopy);
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,7 +47,7 @@ public class TestMultipartEntityBuilder {
|
||||||
final MultipartFormEntity entity = MultipartEntityBuilder.create().buildEntity();
|
final MultipartFormEntity entity = MultipartEntityBuilder.create().buildEntity();
|
||||||
Assert.assertNotNull(entity);
|
Assert.assertNotNull(entity);
|
||||||
Assert.assertTrue(entity.getMultipart() instanceof HttpStrictMultipart);
|
Assert.assertTrue(entity.getMultipart() instanceof HttpStrictMultipart);
|
||||||
Assert.assertEquals(0, entity.getMultipart().getBodyParts().size());
|
Assert.assertEquals(0, entity.getMultipart().getParts().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -72,7 +72,7 @@ public class TestMultipartEntityBuilder {
|
||||||
.addBinaryBody("p4", new ByteArrayInputStream(new byte[]{}))
|
.addBinaryBody("p4", new ByteArrayInputStream(new byte[]{}))
|
||||||
.buildEntity();
|
.buildEntity();
|
||||||
Assert.assertNotNull(entity);
|
Assert.assertNotNull(entity);
|
||||||
final List<FormBodyPart> bodyParts = entity.getMultipart().getBodyParts();
|
final List<MultipartPart> bodyParts = entity.getMultipart().getParts();
|
||||||
Assert.assertNotNull(bodyParts);
|
Assert.assertNotNull(bodyParts);
|
||||||
Assert.assertEquals(4, bodyParts.size());
|
Assert.assertEquals(4, bodyParts.size());
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,7 @@ public class TestMultipartForm {
|
||||||
"field3",
|
"field3",
|
||||||
new StringBody("all kind of stuff", ContentType.DEFAULT_TEXT)).build();
|
new StringBody("all kind of stuff", ContentType.DEFAULT_TEXT)).build();
|
||||||
final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo",
|
final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo",
|
||||||
Arrays.asList(p1, p2, p3));
|
Arrays.<MultipartPart>asList(p1, p2, p3));
|
||||||
|
|
||||||
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
multipart.writeTo(out);
|
multipart.writeTo(out);
|
||||||
|
@ -102,7 +102,7 @@ public class TestMultipartForm {
|
||||||
"field2",
|
"field2",
|
||||||
new StringBody("that stuff", ContentType.parse("stuff/plain; param=value"))).build();
|
new StringBody("that stuff", ContentType.parse("stuff/plain; param=value"))).build();
|
||||||
final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo",
|
final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo",
|
||||||
Arrays.asList(p1, p2));
|
Arrays.<MultipartPart>asList(p1, p2));
|
||||||
|
|
||||||
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
multipart.writeTo(out);
|
multipart.writeTo(out);
|
||||||
|
@ -140,7 +140,7 @@ public class TestMultipartForm {
|
||||||
"field2",
|
"field2",
|
||||||
new InputStreamBody(new FileInputStream(tmpfile), "file.tmp")).build();
|
new InputStreamBody(new FileInputStream(tmpfile), "file.tmp")).build();
|
||||||
final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo",
|
final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo",
|
||||||
Arrays.asList(p1, p2));
|
Arrays.<MultipartPart>asList(p1, p2));
|
||||||
|
|
||||||
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
multipart.writeTo(out);
|
multipart.writeTo(out);
|
||||||
|
@ -183,7 +183,7 @@ public class TestMultipartForm {
|
||||||
"field3",
|
"field3",
|
||||||
new InputStreamBody(new FileInputStream(tmpfile), "file.tmp")).build();
|
new InputStreamBody(new FileInputStream(tmpfile), "file.tmp")).build();
|
||||||
final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo",
|
final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo",
|
||||||
Arrays.asList(p1, p2, p3));
|
Arrays.<MultipartPart>asList(p1, p2, p3));
|
||||||
|
|
||||||
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
multipart.writeTo(out);
|
multipart.writeTo(out);
|
||||||
|
@ -232,7 +232,7 @@ public class TestMultipartForm {
|
||||||
"field3",
|
"field3",
|
||||||
new InputStreamBody(new FileInputStream(tmpfile), "file.tmp")).build();
|
new InputStreamBody(new FileInputStream(tmpfile), "file.tmp")).build();
|
||||||
final HttpRFC6532Multipart multipart = new HttpRFC6532Multipart(null, "foo",
|
final HttpRFC6532Multipart multipart = new HttpRFC6532Multipart(null, "foo",
|
||||||
Arrays.asList(p1, p2, p3));
|
Arrays.<MultipartPart>asList(p1, p2, p3));
|
||||||
|
|
||||||
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
multipart.writeTo(out);
|
multipart.writeTo(out);
|
||||||
|
@ -302,7 +302,7 @@ public class TestMultipartForm {
|
||||||
new InputStreamBody(new FileInputStream(tmpfile), s2 + ".tmp")).build();
|
new InputStreamBody(new FileInputStream(tmpfile), s2 + ".tmp")).build();
|
||||||
final HttpBrowserCompatibleMultipart multipart = new HttpBrowserCompatibleMultipart(
|
final HttpBrowserCompatibleMultipart multipart = new HttpBrowserCompatibleMultipart(
|
||||||
StandardCharsets.UTF_8, "foo",
|
StandardCharsets.UTF_8, "foo",
|
||||||
Arrays.asList(p1, p2));
|
Arrays.<MultipartPart>asList(p1, p2));
|
||||||
|
|
||||||
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
multipart.writeTo(out);
|
multipart.writeTo(out);
|
||||||
|
@ -339,7 +339,7 @@ public class TestMultipartForm {
|
||||||
"field2",
|
"field2",
|
||||||
new StringBody(s2, ContentType.create("text/plain", Charset.forName("KOI8-R")))).build();
|
new StringBody(s2, ContentType.create("text/plain", Charset.forName("KOI8-R")))).build();
|
||||||
final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo",
|
final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo",
|
||||||
Arrays.asList(p1, p2));
|
Arrays.<MultipartPart>asList(p1, p2));
|
||||||
|
|
||||||
final ByteArrayOutputStream out1 = new ByteArrayOutputStream();
|
final ByteArrayOutputStream out1 = new ByteArrayOutputStream();
|
||||||
multipart.writeTo(out1);
|
multipart.writeTo(out1);
|
||||||
|
|
|
@ -54,7 +54,7 @@ public class TestMultipartFormHttpEntity {
|
||||||
final String contentType = entity.getContentType();
|
final String contentType = entity.getContentType();
|
||||||
final HeaderElement elem = BasicHeaderValueParser.INSTANCE.parseHeaderElement(contentType,
|
final HeaderElement elem = BasicHeaderValueParser.INSTANCE.parseHeaderElement(contentType,
|
||||||
new ParserCursor(0, contentType.length()));
|
new ParserCursor(0, contentType.length()));
|
||||||
Assert.assertEquals("multipart/form-data", elem.getName());
|
Assert.assertEquals("multipart/mixed", elem.getName());
|
||||||
final NameValuePair p1 = elem.getParameterByName("boundary");
|
final NameValuePair p1 = elem.getParameterByName("boundary");
|
||||||
Assert.assertNotNull(p1);
|
Assert.assertNotNull(p1);
|
||||||
Assert.assertEquals("whatever", p1.getValue());
|
Assert.assertEquals("whatever", p1.getValue());
|
||||||
|
@ -70,7 +70,7 @@ public class TestMultipartFormHttpEntity {
|
||||||
final String contentType = entity.getContentType();
|
final String contentType = entity.getContentType();
|
||||||
final HeaderElement elem = BasicHeaderValueParser.INSTANCE.parseHeaderElement(contentType,
|
final HeaderElement elem = BasicHeaderValueParser.INSTANCE.parseHeaderElement(contentType,
|
||||||
new ParserCursor(0, contentType.length()));
|
new ParserCursor(0, contentType.length()));
|
||||||
Assert.assertEquals("multipart/form-data", elem.getName());
|
Assert.assertEquals("multipart/mixed", elem.getName());
|
||||||
final NameValuePair p1 = elem.getParameterByName("boundary");
|
final NameValuePair p1 = elem.getParameterByName("boundary");
|
||||||
Assert.assertNotNull(p1);
|
Assert.assertNotNull(p1);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,332 @@
|
||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* 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.
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many
|
||||||
|
* individuals on behalf of the Apache Software Foundation. For more
|
||||||
|
* information on the Apache Software Foundation, please see
|
||||||
|
* <http://www.apache.org/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.hc.client5.http.entity.mime;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import org.apache.hc.core5.http.ContentType;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class TestMultipartMixed {
|
||||||
|
|
||||||
|
private File tmpfile;
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void cleanup() {
|
||||||
|
if (tmpfile != null) {
|
||||||
|
tmpfile.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultipartPartStringParts() throws Exception {
|
||||||
|
final MultipartPart p1 = MultipartPartBuilder.create(
|
||||||
|
new StringBody("this stuff", ContentType.DEFAULT_TEXT)).build();
|
||||||
|
final MultipartPart p2 = MultipartPartBuilder.create(
|
||||||
|
new StringBody("that stuff", ContentType.create(
|
||||||
|
ContentType.TEXT_PLAIN.getMimeType(), StandardCharsets.UTF_8))).build();
|
||||||
|
final MultipartPart p3 = MultipartPartBuilder.create(
|
||||||
|
new StringBody("all kind of stuff", ContentType.DEFAULT_TEXT)).build();
|
||||||
|
final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo",
|
||||||
|
Arrays.<MultipartPart>asList(p1, p2, p3));
|
||||||
|
|
||||||
|
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
multipart.writeTo(out);
|
||||||
|
out.close();
|
||||||
|
|
||||||
|
final String expected =
|
||||||
|
"--foo\r\n" +
|
||||||
|
"Content-Type: text/plain; charset=ISO-8859-1\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
"this stuff\r\n" +
|
||||||
|
"--foo\r\n" +
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
"that stuff\r\n" +
|
||||||
|
"--foo\r\n" +
|
||||||
|
"Content-Type: text/plain; charset=ISO-8859-1\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
"all kind of stuff\r\n" +
|
||||||
|
"--foo--\r\n";
|
||||||
|
final String s = out.toString("US-ASCII");
|
||||||
|
Assert.assertEquals(expected, s);
|
||||||
|
Assert.assertEquals(s.length(), multipart.getTotalLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultipartPartCustomContentType() throws Exception {
|
||||||
|
final MultipartPart p1 = MultipartPartBuilder.create(
|
||||||
|
new StringBody("this stuff", ContentType.DEFAULT_TEXT)).build();
|
||||||
|
final MultipartPart p2 = MultipartPartBuilder.create(
|
||||||
|
new StringBody("that stuff", ContentType.parse("stuff/plain; param=value"))).build();
|
||||||
|
final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo",
|
||||||
|
Arrays.<MultipartPart>asList(p1, p2));
|
||||||
|
|
||||||
|
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
multipart.writeTo(out);
|
||||||
|
out.close();
|
||||||
|
|
||||||
|
final String expected =
|
||||||
|
"--foo\r\n" +
|
||||||
|
"Content-Type: text/plain; charset=ISO-8859-1\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
"this stuff\r\n" +
|
||||||
|
"--foo\r\n" +
|
||||||
|
"Content-Type: stuff/plain; param=value\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
"that stuff\r\n" +
|
||||||
|
"--foo--\r\n";
|
||||||
|
final String s = out.toString("US-ASCII");
|
||||||
|
Assert.assertEquals(expected, s);
|
||||||
|
Assert.assertEquals(s.length(), multipart.getTotalLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultipartPartBinaryParts() throws Exception {
|
||||||
|
tmpfile = File.createTempFile("tmp", ".bin");
|
||||||
|
try (Writer writer = new FileWriter(tmpfile)) {
|
||||||
|
writer.append("some random whatever");
|
||||||
|
}
|
||||||
|
|
||||||
|
final MultipartPart p1 = MultipartPartBuilder.create(
|
||||||
|
new FileBody(tmpfile)).build();
|
||||||
|
@SuppressWarnings("resource")
|
||||||
|
final MultipartPart p2 = MultipartPartBuilder.create(
|
||||||
|
new InputStreamBody(new FileInputStream(tmpfile), "file.tmp")).build();
|
||||||
|
final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo",
|
||||||
|
Arrays.<MultipartPart>asList(p1, p2));
|
||||||
|
|
||||||
|
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
multipart.writeTo(out);
|
||||||
|
out.close();
|
||||||
|
|
||||||
|
final String expected =
|
||||||
|
"--foo\r\n" +
|
||||||
|
"Content-Type: application/octet-stream\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
"some random whatever\r\n" +
|
||||||
|
"--foo\r\n" +
|
||||||
|
"Content-Type: application/octet-stream\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
"some random whatever\r\n" +
|
||||||
|
"--foo--\r\n";
|
||||||
|
final String s = out.toString("US-ASCII");
|
||||||
|
Assert.assertEquals(expected, s);
|
||||||
|
Assert.assertEquals(-1, multipart.getTotalLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultipartPartStrict() throws Exception {
|
||||||
|
tmpfile = File.createTempFile("tmp", ".bin");
|
||||||
|
try (Writer writer = new FileWriter(tmpfile)) {
|
||||||
|
writer.append("some random whatever");
|
||||||
|
}
|
||||||
|
|
||||||
|
final MultipartPart p1 = MultipartPartBuilder.create(
|
||||||
|
new FileBody(tmpfile)).build();
|
||||||
|
final MultipartPart p2 = MultipartPartBuilder.create(
|
||||||
|
new FileBody(tmpfile, ContentType.create("text/plain", "ANSI_X3.4-1968"), "test-file")).build();
|
||||||
|
@SuppressWarnings("resource")
|
||||||
|
final MultipartPart p3 = MultipartPartBuilder.create(
|
||||||
|
new InputStreamBody(new FileInputStream(tmpfile), "file.tmp")).build();
|
||||||
|
final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo",
|
||||||
|
Arrays.<MultipartPart>asList(p1, p2, p3));
|
||||||
|
|
||||||
|
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
multipart.writeTo(out);
|
||||||
|
out.close();
|
||||||
|
|
||||||
|
final String expected =
|
||||||
|
"--foo\r\n" +
|
||||||
|
"Content-Type: application/octet-stream\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
"some random whatever\r\n" +
|
||||||
|
"--foo\r\n" +
|
||||||
|
"Content-Type: text/plain; charset=US-ASCII\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
"some random whatever\r\n" +
|
||||||
|
"--foo\r\n" +
|
||||||
|
"Content-Type: application/octet-stream\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
"some random whatever\r\n" +
|
||||||
|
"--foo--\r\n";
|
||||||
|
final String s = out.toString("US-ASCII");
|
||||||
|
Assert.assertEquals(expected, s);
|
||||||
|
Assert.assertEquals(-1, multipart.getTotalLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultipartPartRFC6532() throws Exception {
|
||||||
|
tmpfile = File.createTempFile("tmp", ".bin");
|
||||||
|
try (Writer writer = new FileWriter(tmpfile)) {
|
||||||
|
writer.append("some random whatever");
|
||||||
|
}
|
||||||
|
|
||||||
|
final MultipartPart p1 = MultipartPartBuilder.create(
|
||||||
|
new FileBody(tmpfile)).build();
|
||||||
|
final MultipartPart p2 = MultipartPartBuilder.create(
|
||||||
|
new FileBody(tmpfile, ContentType.create("text/plain", "ANSI_X3.4-1968"), "test-file")).build();
|
||||||
|
@SuppressWarnings("resource")
|
||||||
|
final MultipartPart p3 = MultipartPartBuilder.create(
|
||||||
|
new InputStreamBody(new FileInputStream(tmpfile), "file.tmp")).build();
|
||||||
|
final HttpRFC6532Multipart multipart = new HttpRFC6532Multipart(null, "foo",
|
||||||
|
Arrays.<MultipartPart>asList(p1, p2, p3));
|
||||||
|
|
||||||
|
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
multipart.writeTo(out);
|
||||||
|
out.close();
|
||||||
|
|
||||||
|
final String expected =
|
||||||
|
"--foo\r\n" +
|
||||||
|
"Content-Type: application/octet-stream\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
"some random whatever\r\n" +
|
||||||
|
"--foo\r\n" +
|
||||||
|
"Content-Type: text/plain; charset=US-ASCII\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
"some random whatever\r\n" +
|
||||||
|
"--foo\r\n" +
|
||||||
|
"Content-Type: application/octet-stream\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
"some random whatever\r\n" +
|
||||||
|
"--foo--\r\n";
|
||||||
|
final String s = out.toString("UTF-8");
|
||||||
|
Assert.assertEquals(expected, s);
|
||||||
|
Assert.assertEquals(-1, multipart.getTotalLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int SWISS_GERMAN_HELLO [] = {
|
||||||
|
0x47, 0x72, 0xFC, 0x65, 0x7A, 0x69, 0x5F, 0x7A, 0xE4, 0x6D, 0xE4
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final int RUSSIAN_HELLO [] = {
|
||||||
|
0x412, 0x441, 0x435, 0x43C, 0x5F, 0x43F, 0x440, 0x438,
|
||||||
|
0x432, 0x435, 0x442
|
||||||
|
};
|
||||||
|
|
||||||
|
private static String constructString(final int [] unicodeChars) {
|
||||||
|
final StringBuilder buffer = new StringBuilder();
|
||||||
|
if (unicodeChars != null) {
|
||||||
|
for (final int unicodeChar : unicodeChars) {
|
||||||
|
buffer.append((char)unicodeChar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultipartPartBrowserCompatibleNonASCIIHeaders() throws Exception {
|
||||||
|
final String s1 = constructString(SWISS_GERMAN_HELLO);
|
||||||
|
final String s2 = constructString(RUSSIAN_HELLO);
|
||||||
|
|
||||||
|
tmpfile = File.createTempFile("tmp", ".bin");
|
||||||
|
try (Writer writer = new FileWriter(tmpfile)) {
|
||||||
|
writer.append("some random whatever");
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("resource")
|
||||||
|
final MultipartPart p1 = MultipartPartBuilder.create(
|
||||||
|
new InputStreamBody(new FileInputStream(tmpfile), s1 + ".tmp")).build();
|
||||||
|
@SuppressWarnings("resource")
|
||||||
|
final MultipartPart p2 = MultipartPartBuilder.create(
|
||||||
|
new InputStreamBody(new FileInputStream(tmpfile), s2 + ".tmp")).build();
|
||||||
|
final HttpBrowserCompatibleMultipart multipart = new HttpBrowserCompatibleMultipart(
|
||||||
|
StandardCharsets.UTF_8, "foo",
|
||||||
|
Arrays.<MultipartPart>asList(p1, p2));
|
||||||
|
|
||||||
|
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
multipart.writeTo(out);
|
||||||
|
out.close();
|
||||||
|
|
||||||
|
final String expected =
|
||||||
|
"--foo\r\n" +
|
||||||
|
"Content-Type: application/octet-stream\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
"some random whatever\r\n" +
|
||||||
|
"--foo\r\n" +
|
||||||
|
"Content-Type: application/octet-stream\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
"some random whatever\r\n" +
|
||||||
|
"--foo--\r\n";
|
||||||
|
final String s = out.toString("UTF-8");
|
||||||
|
Assert.assertEquals(expected, s);
|
||||||
|
Assert.assertEquals(-1, multipart.getTotalLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultipartPartStringPartsMultiCharsets() throws Exception {
|
||||||
|
final String s1 = constructString(SWISS_GERMAN_HELLO);
|
||||||
|
final String s2 = constructString(RUSSIAN_HELLO);
|
||||||
|
|
||||||
|
final MultipartPart p1 = MultipartPartBuilder.create(
|
||||||
|
new StringBody(s1, ContentType.create("text/plain", Charset.forName("ISO-8859-1")))).build();
|
||||||
|
final MultipartPart p2 = MultipartPartBuilder.create(
|
||||||
|
new StringBody(s2, ContentType.create("text/plain", Charset.forName("KOI8-R")))).build();
|
||||||
|
final HttpStrictMultipart multipart = new HttpStrictMultipart(null, "foo",
|
||||||
|
Arrays.<MultipartPart>asList(p1, p2));
|
||||||
|
|
||||||
|
final ByteArrayOutputStream out1 = new ByteArrayOutputStream();
|
||||||
|
multipart.writeTo(out1);
|
||||||
|
out1.close();
|
||||||
|
|
||||||
|
final ByteArrayOutputStream out2 = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
out2.write((
|
||||||
|
"--foo\r\n" +
|
||||||
|
"Content-Type: text/plain; charset=ISO-8859-1\r\n" +
|
||||||
|
"\r\n").getBytes(StandardCharsets.US_ASCII));
|
||||||
|
out2.write(s1.getBytes(StandardCharsets.ISO_8859_1));
|
||||||
|
out2.write(("\r\n" +
|
||||||
|
"--foo\r\n" +
|
||||||
|
"Content-Type: text/plain; charset=KOI8-R\r\n" +
|
||||||
|
"\r\n").getBytes(StandardCharsets.US_ASCII));
|
||||||
|
out2.write(s2.getBytes(Charset.forName("KOI8-R")));
|
||||||
|
out2.write(("\r\n" +
|
||||||
|
"--foo--\r\n").getBytes(StandardCharsets.US_ASCII));
|
||||||
|
out2.close();
|
||||||
|
|
||||||
|
final byte[] actual = out1.toByteArray();
|
||||||
|
final byte[] expected = out2.toByteArray();
|
||||||
|
|
||||||
|
Assert.assertEquals(expected.length, actual.length);
|
||||||
|
for (int i = 0; i < actual.length; i++) {
|
||||||
|
Assert.assertEquals(expected[i], actual[i]);
|
||||||
|
}
|
||||||
|
Assert.assertEquals(expected.length, multipart.getTotalLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,157 @@
|
||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* 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.
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
* This software consists of voluntary contributions made by many
|
||||||
|
* individuals on behalf of the Apache Software Foundation. For more
|
||||||
|
* information on the Apache Software Foundation, please see
|
||||||
|
* <http://www.apache.org/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.hc.client5.http.entity.mime;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.hc.core5.http.ContentType;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class TestMultipartPartBuilder {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBuildBodyPartBasics() throws Exception {
|
||||||
|
final StringBody stringBody = new StringBody("stuff", ContentType.TEXT_PLAIN);
|
||||||
|
final MultipartPart part = MultipartPartBuilder.create()
|
||||||
|
.setBody(stringBody)
|
||||||
|
.build();
|
||||||
|
Assert.assertNotNull(part);
|
||||||
|
Assert.assertEquals(stringBody, part.getBody());
|
||||||
|
final Header header = part.getHeader();
|
||||||
|
Assert.assertNotNull(header);
|
||||||
|
assertFields(Arrays.asList(
|
||||||
|
new MinimalField("Content-Type", "text/plain; charset=ISO-8859-1")),
|
||||||
|
header.getFields());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBuildBodyPartMultipleBuilds() throws Exception {
|
||||||
|
final StringBody stringBody = new StringBody("stuff", ContentType.TEXT_PLAIN);
|
||||||
|
final MultipartPartBuilder builder = MultipartPartBuilder.create();
|
||||||
|
final MultipartPart part1 = builder
|
||||||
|
.setBody(stringBody)
|
||||||
|
.build();
|
||||||
|
Assert.assertNotNull(part1);
|
||||||
|
Assert.assertEquals(stringBody, part1.getBody());
|
||||||
|
final Header header1 = part1.getHeader();
|
||||||
|
Assert.assertNotNull(header1);
|
||||||
|
assertFields(Arrays.asList(
|
||||||
|
new MinimalField("Content-Type", "text/plain; charset=ISO-8859-1")),
|
||||||
|
header1.getFields());
|
||||||
|
final FileBody fileBody = new FileBody(new File("/path/stuff.bin"), ContentType.DEFAULT_BINARY);
|
||||||
|
final MultipartPart part2 = builder
|
||||||
|
.setBody(fileBody)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Assert.assertNotNull(part2);
|
||||||
|
Assert.assertEquals(fileBody, part2.getBody());
|
||||||
|
final Header header2 = part2.getHeader();
|
||||||
|
Assert.assertNotNull(header2);
|
||||||
|
assertFields(Arrays.asList(
|
||||||
|
new MinimalField("Content-Type", "application/octet-stream")),
|
||||||
|
header2.getFields());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBuildBodyPartCustomHeaders() throws Exception {
|
||||||
|
final StringBody stringBody = new StringBody("stuff", ContentType.TEXT_PLAIN);
|
||||||
|
final MultipartPartBuilder builder = MultipartPartBuilder.create(stringBody);
|
||||||
|
final MultipartPart part1 = builder
|
||||||
|
.addHeader("header1", "blah")
|
||||||
|
.addHeader("header3", "blah")
|
||||||
|
.addHeader("header3", "blah")
|
||||||
|
.addHeader("header3", "blah")
|
||||||
|
.addHeader("header3", "blah")
|
||||||
|
.addHeader("header3", "blah")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Assert.assertNotNull(part1);
|
||||||
|
final Header header1 = part1.getHeader();
|
||||||
|
Assert.assertNotNull(header1);
|
||||||
|
|
||||||
|
assertFields(Arrays.asList(
|
||||||
|
new MinimalField("header1", "blah"),
|
||||||
|
new MinimalField("header3", "blah"),
|
||||||
|
new MinimalField("header3", "blah"),
|
||||||
|
new MinimalField("header3", "blah"),
|
||||||
|
new MinimalField("header3", "blah"),
|
||||||
|
new MinimalField("header3", "blah"),
|
||||||
|
new MinimalField("Content-Type", "text/plain; charset=ISO-8859-1")),
|
||||||
|
header1.getFields());
|
||||||
|
|
||||||
|
final MultipartPart part2 = builder
|
||||||
|
.addHeader("header2", "yada")
|
||||||
|
.removeHeaders("header3")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Assert.assertNotNull(part2);
|
||||||
|
final Header header2 = part2.getHeader();
|
||||||
|
Assert.assertNotNull(header2);
|
||||||
|
|
||||||
|
assertFields(Arrays.asList(
|
||||||
|
new MinimalField("header1", "blah"),
|
||||||
|
new MinimalField("header2", "yada"),
|
||||||
|
new MinimalField("Content-Type", "text/plain; charset=ISO-8859-1")),
|
||||||
|
header2.getFields());
|
||||||
|
|
||||||
|
final MultipartPart part3 = builder
|
||||||
|
.addHeader("Content-Disposition", "disposition stuff")
|
||||||
|
.addHeader("Content-Type", "type stuff")
|
||||||
|
.addHeader("Content-Transfer-Encoding", "encoding stuff")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Assert.assertNotNull(part3);
|
||||||
|
final Header header3 = part3.getHeader();
|
||||||
|
Assert.assertNotNull(header3);
|
||||||
|
|
||||||
|
assertFields(Arrays.asList(
|
||||||
|
new MinimalField("header1", "blah"),
|
||||||
|
new MinimalField("header2", "yada"),
|
||||||
|
new MinimalField("Content-Disposition", "disposition stuff"),
|
||||||
|
new MinimalField("Content-Type", "type stuff"),
|
||||||
|
new MinimalField("Content-Transfer-Encoding", "encoding stuff")),
|
||||||
|
header3.getFields());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertFields(final List<MinimalField> expected, final List<MinimalField> result) {
|
||||||
|
Assert.assertNotNull(result);
|
||||||
|
Assert.assertEquals(expected.size(), result.size());
|
||||||
|
for (int i = 0; i < expected.size(); i++) {
|
||||||
|
final MinimalField expectedField = expected.get(i);
|
||||||
|
final MinimalField resultField = result.get(i);
|
||||||
|
Assert.assertNotNull(resultField);
|
||||||
|
Assert.assertEquals(expectedField.getName(), resultField.getName());
|
||||||
|
Assert.assertEquals(expectedField.getBody(), resultField.getBody());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue