Skip to content
Draft
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
@@ -0,0 +1,74 @@
/*
* Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package org.glassfish.jersey.netty.connector;

import org.glassfish.jersey.client.ClientRequest;
import org.glassfish.jersey.client.ClientResponse;

import javax.ws.rs.core.MediaType;
import java.lang.annotation.Annotation;

/**
* A NettyHttpRedirectController implementation that allows attaching a file only after a redirect.
* This controller can be configured to hold a file entity that will only be attached to the request
* after a redirect has occurred.
*
* @since 2.47
*/
public class FileAttachingRedirectController extends NettyHttpRedirectController {

/**
* Property name for the file entity to be attached after a redirect.
*/
public static final String FILE_ENTITY_AFTER_REDIRECT
= "jersey.config.client.netty.file.entity.after.redirect";

/**
* Property name for the file entity media type to be used after a redirect.
*/
public static final String FILE_ENTITY_MEDIA_TYPE_AFTER_REDIRECT
= "jersey.config.client.netty.file.entity.media.type.after.redirect";

/**
* Property name for the file entity annotations to be used after a redirect.
*/
public static final String FILE_ENTITY_ANNOTATIONS_AFTER_REDIRECT
= "jersey.config.client.netty.file.entity.annotations.after.redirect";

@Override
public boolean prepareRedirect(ClientRequest request, ClientResponse response) {
boolean result = super.prepareRedirect(request, response);

if (result) {
final Object fileEntity = request.getProperty(FILE_ENTITY_AFTER_REDIRECT);
if (fileEntity != null) {
final MediaType mediaType = (MediaType) request.getProperty(FILE_ENTITY_MEDIA_TYPE_AFTER_REDIRECT);
final Annotation[] annotations = (Annotation[]) request.getProperty(FILE_ENTITY_ANNOTATIONS_AFTER_REDIRECT);
request.setEntity(fileEntity, annotations);
if (mediaType != null) {
request.setMediaType(mediaType);
}

request.removeProperty(FILE_ENTITY_AFTER_REDIRECT);
request.removeProperty(FILE_ENTITY_MEDIA_TYPE_AFTER_REDIRECT);
request.removeProperty(FILE_ENTITY_ANNOTATIONS_AFTER_REDIRECT);
}
}

return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import org.glassfish.jersey.message.internal.ReaderWriter;

import java.io.BufferedReader;
import java.io.IOException;
Expand Down Expand Up @@ -80,12 +81,8 @@ public void handle(HttpExchange exchange) throws IOException {
return;
}

final BufferedReader reader
= new BufferedReader(new InputStreamReader(exchange.getRequestBody(), StandardCharsets.UTF_8));
while (reader.readLine() != null) {
//discard payload - required for JDK 1.8
}
reader.close();
//discard payload - required for JDK 1.8
ReaderWriter.readFromAsBytes(exchange.getRequestBody());

// Send a 307 Temporary Redirect to /upload
// This preserves the POST method and body in the redirect
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataMultiPart;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.netty.connector.FileAttachingRedirectController;
import org.glassfish.jersey.netty.connector.NettyClientProperties;
import org.glassfish.jersey.netty.connector.NettyConnectorProvider;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
Expand All @@ -47,13 +47,22 @@ public class RedirectLargeFileTest {
private static final int SERVER_PORT = 9997;
private static final String SERVER_ADDR = String.format("http://localhost:%d/submit", SERVER_PORT);

Client client() {
Client client(boolean registerControler) {
final ClientConfig config = new ClientConfig();
config.connectorProvider(new NettyConnectorProvider());
config.register(MultiPartFeature.class);
if (registerControler) {
registerFileUploadController(config);
}
return ClientBuilder.newClient(config);
}

void registerFileUploadController(ClientConfig config) {
// Register custom redirect controller
FileAttachingRedirectController redirectController = new FileAttachingRedirectController();
config.property(NettyClientProperties.HTTP_REDIRECT_CONTROLLER, redirectController);
}

@BeforeAll
static void startServer() throws Exception{
RedirectFileUploadServerTest.start(SERVER_PORT);
Expand All @@ -78,13 +87,46 @@ void sendFileTest() throws Exception {

final byte[] content = Files.readAllBytes(realFilePath);

final FormDataMultiPart mp = new FormDataMultiPart();
mp.bodyPart(new FormDataBodyPart(FormDataContentDisposition.name(fileName).fileName(fileName).build(),
content,
MediaType.TEXT_PLAIN_TYPE));

try (final Response response = client(false).target(SERVER_ADDR).request()
.post(Entity.entity(mp, MediaType.MULTIPART_FORM_DATA_TYPE))) {
Assertions.assertEquals(200, response.getStatus());
}
} finally {
Files.deleteIfExists(pathResource);
}
}

@Test
void sendFileAfterRedirectTest() throws Exception {
final String fileName = "bigFile.json";
final String path = "target/" + fileName;

final Path pathResource = Paths.get(path);
try {
final Path realFilePath = Files.createFile(pathResource.toAbsolutePath());

generateJson(realFilePath.toString(), 1000000); // 33Mb real file size

final byte[] content = Files.readAllBytes(realFilePath);

// Create the multipart form data
final FormDataMultiPart mp = new FormDataMultiPart();
mp.bodyPart(new FormDataBodyPart(FormDataContentDisposition.name(fileName).fileName(fileName).build(),
content,
MediaType.TEXT_PLAIN_TYPE));

try (final Response response = client().target(SERVER_ADDR).request()
.post(Entity.entity(mp, MediaType.MULTIPART_FORM_DATA_TYPE))) {
// Set the file entity to be attached after the redirect on the request properties
final Response response = client(true).target(SERVER_ADDR).request()
.property(FileAttachingRedirectController.FILE_ENTITY_AFTER_REDIRECT, mp)
.property(FileAttachingRedirectController.FILE_ENTITY_MEDIA_TYPE_AFTER_REDIRECT,
MediaType.MULTIPART_FORM_DATA_TYPE)
.post(Entity.entity("", MediaType.TEXT_PLAIN_TYPE));
if (response != null){
Assertions.assertEquals(200, response.getStatus());
}
} finally {
Expand Down
Loading