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
2 changes: 2 additions & 0 deletions changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,8 @@ Changes log
entity stream only, and is ignored after the head of a response announcing an empty
body. The failures caused by stopping the client report "Client stopped". Issue #1601.
- The `Max-Age` and `Path` attributes of a cookie setting are no longer quoted. Issue #1334.
- `MultiPartRepresentation.setBoundary` kept only the boundary parameter when replacing an
existing boundary, dropping the media type's other parameters. Issue #1600.
- `Restlet.handle()` no longer fails with a `NullPointerException` when the Restlet has no context and cannot be started.
- `CookieAuthenticator` no longer lets the next restlet overwrite the redirection sent after
a login. Issue #554.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,8 @@
}

/**
* Sets a boundary to an existing media type. If the original media-type already has a
* "boundary" parameter, it will be erased. *
* Sets a boundary to an existing media type. If the original media type already has a
* "boundary" parameter, its value is replaced. The other parameters are kept.
*
* @param mediaType The media type to update.
* @param boundary The boundary to add as a parameter.
Expand All @@ -212,13 +212,57 @@
MediaType result = null;

if (mediaType != null) {
if (mediaType.getParameters().getFirst(PARAMETER_BOUNDARY) != null) {
result = new MediaType(mediaType.getParent(), PARAMETER_BOUNDARY, boundary);
} else {
result = new MediaType(mediaType, PARAMETER_BOUNDARY, boundary);
result =
new MediaType(
new MediaType(
withoutBoundary(mediaType.getName()),
mediaType.getDescription()),
PARAMETER_BOUNDARY,
boundary);
}

return result;
}

/**
* Removes the boundary parameters from a media type name, which holds all the parameters,
* keeping the others as written. A null name, accepted by {@link MediaType}, is returned as is.
*/
private static String withoutBoundary(String name) {
int index = (name == null) ? -1 : name.indexOf(';');
if (index < 0) {
return name;
}

StringBuilder result = new StringBuilder(name.substring(0, index));
for (String parameter : splitParameters(name.substring(index + 1))) {
String trimmed = parameter.trim();
int equals = trimmed.indexOf('=');
String parameterName = (equals < 0) ? trimmed : trimmed.substring(0, equals).trim();
if (!trimmed.isEmpty() && !PARAMETER_BOUNDARY.equalsIgnoreCase(parameterName)) {
result.append("; ").append(trimmed);
}
}
return result.toString();
}

/** Splits parameters on ';', except inside quoted strings. */
private static List<String> splitParameters(String parameters) {
List<String> result = new ArrayList<>();
boolean quoted = false;
int start = 0;
for (int i = 0; i < parameters.length(); i++) {
char c = parameters.charAt(i);
if (quoted && c == '\\') {
i++;

Check warning on line 257 in org.restlet/src/main/java/org/restlet/representation/MultiPartRepresentation.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor the code in order to not assign to this loop counter from within the loop body.

See more on https://sonarcloud.io/project/issues?id=restlet_restlet-framework-java&issues=AaGNm-hNc8TYGq6r6nv5&open=AaGNm-hNc8TYGq6r6nv5&pullRequest=1606
} else if (c == '"') {
quoted = !quoted;
} else if (c == ';' && !quoted) {
result.add(parameters.substring(start, i));
start = i + 1;
}
}
result.add(parameters.substring(start));
return result;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.restlet.data.MediaType;
import org.restlet.data.Parameter;
import org.restlet.representation.FileRepresentation;
import org.restlet.representation.MultiPartRepresentation;
import org.restlet.representation.MultiPartRepresentation.Part;
import org.restlet.representation.StringRepresentation;
import org.restlet.util.Series;

/**
* Test case for the {@link MultiPartRepresentation} class in multipart mode.
Expand Down Expand Up @@ -77,6 +79,58 @@ void testContentType() {
"multipart/form-data; boundary=myActualBoundary", rep.getMediaType().toString());
}

@Test
void setBoundaryKeepsTheOtherParameters() {
assertEquals(
"multipart/mixed; deferSpec=20220824; boundary=b",
MultiPartRepresentation.setBoundary(
MediaType.valueOf("multipart/mixed; deferSpec=20220824"), "b")
.toString());
assertEquals(
"multipart/mixed; deferSpec=20220824; boundary=b",
MultiPartRepresentation.setBoundary(
MediaType.valueOf(
"multipart/mixed; boundary=a; deferSpec=20220824"),
"b")
.toString());
assertEquals(
"multipart/related; type=\"a;boundary=x\"; boundary=b",
MultiPartRepresentation.setBoundary(
MediaType.valueOf(
"multipart/related; Boundary=a; type=\"a;boundary=x\""),
"b")
.toString());
// As parsed from a Content-Type header
Series<Parameter> parameters = new Series<>(Parameter.class);
parameters.add("type", "application/xml");
parameters.add("BOUNDARY", "a");
assertEquals(
"multipart/related; type=\"application/xml\"; boundary=b",
MultiPartRepresentation.setBoundary(
new MediaType("multipart/related", parameters), "b")
.toString());
assertNull(MultiPartRepresentation.setBoundary(null, "b"));
assertEquals(
MediaType.MULTIPART_FORM_DATA.getDescription(),
MultiPartRepresentation.setBoundary(MediaType.MULTIPART_FORM_DATA, "b")
.getDescription());
assertEquals(
"b",
MultiPartRepresentation.getBoundary(
MultiPartRepresentation.setBoundary(new MediaType((String) null), "b")));

MultiPartRepresentation rep =
new MultiPartRepresentation(
MediaType.valueOf("multipart/mixed; deferSpec=20220824"),
"b",
List.of(new Part("a", null, new StringRepresentation("b"))));
assertEquals(
"multipart/mixed; deferSpec=20220824; boundary=b", rep.getMediaType().toString());
rep.setBoundary("c");
assertEquals(
"multipart/mixed; deferSpec=20220824; boundary=c", rep.getMediaType().toString());
}

@Test
void testRandomBoundary() {
MultiPartRepresentation rep =
Expand Down
Loading