Skip to content
Open
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
Expand Up @@ -33,10 +33,15 @@
import org.asynchttpclient.netty.channel.PrincipalScopedPartitionKey;
import org.asynchttpclient.netty.request.NettyRequestSender;
import io.netty.handler.codec.http2.Http2StreamChannel;
import org.asynchttpclient.request.body.generator.FileBodyGenerator;
import org.asynchttpclient.request.body.multipart.InputStreamPart;
import org.asynchttpclient.request.body.multipart.Part;
import org.asynchttpclient.uri.Uri;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

Expand All @@ -56,7 +61,6 @@
import static org.asynchttpclient.util.HttpConstants.ResponseStatusCodes.SEE_OTHER_303;
import static org.asynchttpclient.util.HttpConstants.ResponseStatusCodes.TEMPORARY_REDIRECT_307;
import static org.asynchttpclient.util.HttpUtils.followRedirect;
import static org.asynchttpclient.util.MiscUtils.isNonEmpty;
import static org.asynchttpclient.util.ThrowableUtil.unknownStackTrace;

public class Redirect30xInterceptor {
Expand Down Expand Up @@ -132,13 +136,28 @@ public boolean exitAfterHandlingRedirect(Channel channel, NettyResponseFuture<?>
LOGGER.debug("Stripping credentials on redirect to {}", newUri);
}

final RequestBuilder requestBuilder = new RequestBuilder(switchToGet ? GET : originalMethod)
.setChannelPoolPartitioning(request.getChannelPoolPartitioning())
final RequestBuilder requestBuilder;
if (keepBody) {
ensureBodyReplayable(request);
requestBuilder = request.toBuilder();
if (!sameBase) {
// An explicitly resolved address and virtual host belong to the previous target.
requestBuilder.setAddress(null);
requestBuilder.setVirtualHost(null);
}
} else {
requestBuilder = new RequestBuilder(switchToGet ? GET : originalMethod)
.setChannelPoolPartitioning(request.getChannelPoolPartitioning())
.setLocalAddress(request.getLocalAddress())
.setNameResolver(request.getNameResolver())
.setProxyServer(request.getProxyServer())
.setRangeOffset(request.getRangeOffset());
}

requestBuilder.setMethod(switchToGet ? GET : originalMethod)
.setFollowRedirect(true)
.setLocalAddress(request.getLocalAddress())
.setNameResolver(request.getNameResolver())
.setProxyServer(request.getProxyServer())
.setRealm(stripAuth ? null : request.getRealm())
.setHeaders(propagatedHeaders(request, realm, keepBody, stripAuth))
.setRequestTimeout(request.getRequestTimeout())
.setReadTimeout(request.getReadTimeout());

Expand All @@ -154,27 +173,10 @@ public boolean exitAfterHandlingRedirect(Channel channel, NettyResponseFuture<?>
if (stripAuth) {
future.setRealm(null);
future.setProxyRealm(null);
// Request.toBuilder copies Cookie objects separately from the Cookie header.
requestBuilder.resetCookies();
}

if (keepBody) {
requestBuilder.setCharset(request.getCharset());
if (isNonEmpty(request.getFormParams())) {
requestBuilder.setFormParams(request.getFormParams());
} else if (request.getStringData() != null) {
requestBuilder.setBody(request.getStringData());
} else if (request.getByteData() != null) {
requestBuilder.setBody(request.getByteData());
} else if (request.getByteBufferData() != null) {
requestBuilder.setBody(request.getByteBufferData());
} else if (request.getBodyGenerator() != null) {
requestBuilder.setBody(request.getBodyGenerator());
} else if (isNonEmpty(request.getBodyParts())) {
requestBuilder.setBodyParts(request.getBodyParts());
}
}

requestBuilder.setHeaders(propagatedHeaders(request, realm, keepBody, stripAuth));

// in case of a redirect from HTTP to HTTPS, future
// attributes might change
final boolean initialConnectionKeepAlive = future.isKeepAlive();
Expand All @@ -192,7 +194,7 @@ public boolean exitAfterHandlingRedirect(Channel channel, NettyResponseFuture<?>
}
}

if (sameBase) {
if (sameBase && !keepBody) {
// we can only assume the virtual host is still valid if the baseUrl is the same
requestBuilder.setVirtualHost(request.getVirtualHost());
}
Expand Down Expand Up @@ -229,10 +231,50 @@ public boolean exitAfterHandlingRedirect(Channel channel, NettyResponseFuture<?>
return false;
}

private static void ensureBodyReplayable(Request request) throws IOException {
for (Part part : request.getBodyParts()) {
if (part instanceof InputStreamPart) {
throw new IOException("Multipart InputStream body part '" + part.getName()
+ "' cannot be replayed after redirect");
}
}

File file = selectedBodyFile(request);
if (file != null && !file.isFile()) {
throw new IOException("Redirect request body file " + file.getAbsolutePath()
+ " is not a file or does not exist");
}
}

private static File selectedBodyFile(Request request) {
// Keep this precedence aligned with NettyRequestFactory.body. A File can remain set alongside a
// higher-priority representation, so only validate it when the original request actually sent it.
if (request.getByteData() != null
|| request.getCompositeByteData() != null
|| request.getStringData() != null
|| request.getByteBufferData() != null
|| request.getByteBufData() != null
|| request.getStreamData() != null
|| !request.getFormParams().isEmpty()
|| !request.getBodyParts().isEmpty()) {
return null;
}
if (request.getFile() != null) {
return request.getFile();
}
return request.getBodyGenerator() instanceof FileBodyGenerator
? ((FileBodyGenerator) request.getBodyGenerator()).getFile()
: null;
}

private static HttpHeaders propagatedHeaders(Request request, Realm realm, boolean keepBody, boolean stripAuthorization) {
HttpHeaders headers = request.getHeaders()
.remove(HOST)
.remove(CONTENT_LENGTH);
HttpHeaders headers = request.getHeaders().copy().remove(HOST);

// A raw InputStream has no intrinsic length from which NettyRequestFactory can rebuild this header.
// Preserve a caller-supplied value when the stream itself is replayed.
if (!keepBody || request.getStreamData() == null) {
headers.remove(CONTENT_LENGTH);
}

if (!keepBody) {
headers.remove(CONTENT_TYPE);
Expand Down
Loading
Loading