Skip to content
Merged
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
7 changes: 6 additions & 1 deletion RELEASE_NOTES.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Release 5.5.1
Release 5.6-alpha1
------------------

Change Log
Expand All @@ -10,6 +10,11 @@ Change Log
* Bump testcontainers.version from 1.20.6 to 1.21.1 #638.
Contributed by Gary Gregory <garydgregory at gmail.com>

* Add org.apache.hc.client5.http.entity.mime.PathBody
Add org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder.addBinaryBody(String, File)
Add org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder.addBinaryBody(String, Path, ContentType, String)
Contributed by Gary Gregory <garydgregory at gmail.com>


Release 5.5
------------------
Expand Down
5 changes: 5 additions & 0 deletions httpclient5/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
Expand Down Expand Up @@ -215,6 +216,32 @@ public MultipartEntityBuilder addBinaryBody(
return addPart(name, new FileBody(file, contentType, filename));
}

/**
* Adds body with contents from the given source Path.
*
* @param name The part name.
* @param path The source path.
* @return {@code this} instance.
* @since 5.6
*/
public MultipartEntityBuilder addBinaryBody(final String name, final Path path) {
return addBinaryBody(name, path, ContentType.DEFAULT_BINARY, path != null ? path.getFileName().toString() : null);
}

/**
* Adds body with contents from the given source Path.
*
* @param name The part name.
* @param path The source path.
* @param contentType The content type.
* @param fileName The file name to override the Path's file name.
* @return {@code this} instance.
* @since 5.6
*/
public MultipartEntityBuilder addBinaryBody(final String name, final Path path, final ContentType contentType, final String fileName) {
return addPart(name, new PathBody(path, contentType, fileName));
}

public MultipartEntityBuilder addBinaryBody(
final String name, final File file) {
return addBinaryBody(name, file, ContentType.DEFAULT_BINARY, file != null ? file.getName() : null);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
* ====================================================================
* 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.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;

import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.util.Args;

/**
* Binary body part backed by an NIO {@link Path}.
*
* @see org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder
*
* @since 5.6
*/
public class PathBody extends AbstractContentBody {

private static String getFileName(final Path path) {
return path != null ? Objects.toString(path.getFileName(), null) : null;
}

private final String fileName;

private final Path path;

/**
* Constructs a new instance for a given Path.
*
* @param path the source Path.
*/
public PathBody(final Path path) {
this(path, ContentType.DEFAULT_BINARY, getFileName(path));
}

/**
* Constructs a new instance for a given Path.
*
* @param path the source Path.
* @param contentType the content type.
*/
public PathBody(final Path path, final ContentType contentType) {
this(path, contentType, getFileName(path));
}

/**
* Constructs a new instance for a given Path.
*
* @param path the source Path.
* @param contentType the content type.
* @param fileName The file name to override the Path's file name.
*/
public PathBody(final Path path, final ContentType contentType, final String fileName) {
super(contentType);
this.path = Args.notNull(path, "path");
this.fileName = fileName == null ? getFileName(path) : fileName;
}

@Override
public long getContentLength() {
try {
return Files.size(path);
} catch (final IOException e) {
throw new UncheckedIOException(e);
}
}

@Override
public String getFilename() {
return fileName;
}

/**
* Gets a new input stream.
*
* @return a new input stream.
* @throws IOException if an I/O error occurs
*/
public InputStream getInputStream() throws IOException {
return Files.newInputStream(path);
}

/**
* Gets the source Path.
*
* @return the source Path.
*/
public Path getPath() {
return path;
}

@Override
public void writeTo(final OutputStream out) throws IOException {
Files.copy(path, Args.notNull(out, "Output stream"));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,31 @@
package org.apache.hc.client5.http.entity.mime;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;

import org.apache.hc.core5.http.ContentType;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

class FormBodyPartTest {

@TempDir
Path tempDir;

@Test
void testConstructorCompat() throws Exception {
final File tmp = File.createTempFile("test", "test");
tmp.deleteOnExit();
void testFileConstructorCompat() throws Exception {
final File tmp = Files.createTempFile(tempDir, "test-", "-file.bin").toFile();
final FileBody obj = new FileBody(tmp, ContentType.APPLICATION_OCTET_STREAM);
Assertions.assertEquals(tmp.getName(), obj.getFilename());
}

@Test
void testPathConstructorCompat() throws Exception {
final Path tmp = Files.createTempFile(tempDir, "test-", "-path.bin");
final PathBody obj = new PathBody(tmp, ContentType.APPLICATION_OCTET_STREAM);
Assertions.assertEquals(tmp.getFileName().toString(), obj.getFilename());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;

Expand All @@ -42,9 +45,13 @@
import org.apache.hc.core5.http.message.ParserCursor;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

class TestMultipartEntityBuilder {

@TempDir
Path tempDir;

@Test
void testBasics() {
final MultipartFormEntity entity = MultipartEntityBuilder.create().buildEntity();
Expand All @@ -67,7 +74,7 @@ void testMultipartOptions() {
}

@Test
void testAddBodyParts() {
void testAddBodyPartsFile() {
final MultipartFormEntity entity = MultipartEntityBuilder.create()
.addTextBody("p1", "stuff")
.addBinaryBody("p2", new File("stuff"))
Expand All @@ -81,6 +88,20 @@ void testAddBodyParts() {
Assertions.assertEquals(5, bodyParts.size());
}

@Test
void testAddBodyPartsPath() throws IOException {
final MultipartFormEntity entity = MultipartEntityBuilder.create()
.addTextBody("p1", "stuff")
.addBinaryBody("p2", Files.createTempFile(tempDir, "test-", ".bin"))
.addBinaryBody("p3", new byte[]{})
.addBinaryBody("p4", new ByteArrayInputStream(new byte[]{}))
.addBinaryBody("p5", new ByteArrayInputStream(new byte[]{}), ContentType.DEFAULT_BINARY, "filename")
.buildEntity();
Assertions.assertNotNull(entity);
final List<MultipartPart> bodyParts = entity.getMultipart().getParts();
Assertions.assertNotNull(bodyParts);
Assertions.assertEquals(5, bodyParts.size());
}

@Test
void testMultipartCustomContentType() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ void testMultipartPartBinaryParts() throws Exception {
}

@Test
void testMultipartPartStrict() throws Exception {
void testMultipartPartFileStrict() throws Exception {
tmpfile = File.createTempFile("tmp", ".bin");
try (Writer writer = new FileWriter(tmpfile)) {
writer.append("some random whatever");
Expand Down Expand Up @@ -189,6 +189,46 @@ void testMultipartPartStrict() throws Exception {
Assertions.assertEquals(-1, multipart.getTotalLength());
}

@Test
void testMultipartPartPathStrict() throws Exception {
tmpfile = File.createTempFile("tmp", ".bin");
try (Writer writer = new FileWriter(tmpfile)) {
writer.append("some random whatever");
}

final MultipartPart p1 = MultipartPartBuilder.create(
new PathBody(tmpfile.toPath())).build();
final MultipartPart p2 = MultipartPartBuilder.create(
new PathBody(tmpfile.toPath(), 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.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");
Assertions.assertEquals(expected, s);
Assertions.assertEquals(-1, multipart.getTotalLength());
}

@Test
void testMultipartPartRFC6532() throws Exception {
tmpfile = File.createTempFile("tmp", ".bin");
Expand Down
Loading