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
6 changes: 5 additions & 1 deletion vertx-core/src/main/asciidoc/http.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1609,9 +1609,13 @@ Alternatively you can just parse the `Set-Cookie` headers yourself in the respon

The client can be configured to follow HTTP redirections provided by the `Location` response header when the client receives:

* a `301`, `302`, `307` or `308` status code along with an HTTP GET or HEAD method
* a `301`, `302`, `307` or `308` status code along with an HTTP GET, HEAD, or QUERY method
* a `303` status code, in addition the directed request perform an HTTP GET method

When redirecting a `QUERY` request, the request body is buffered in memory and resent to the redirected location.
By default, the maximum size of this buffer is limited to `4KB` (configured via {@link io.vertx.core.http.HttpClientConfig#setMaxRedirectBufferSize(int)}).
If the body size exceeds this limit, the redirection is not followed.

Here's an example:

[source,$lang]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@ static void fromJson(Iterable<java.util.Map.Entry<String, Object>> json, HttpCli
obj.setMaxRedirects(((Number)member.getValue()).intValue());
}
break;
case "maxRedirectBufferSize":
if (member.getValue() instanceof Number) {
obj.setMaxRedirectBufferSize(((Number)member.getValue()).intValue());
}
break;
case "forceSni":
if (member.getValue() instanceof Boolean) {
obj.setForceSni((Boolean)member.getValue());
Expand Down Expand Up @@ -193,6 +198,7 @@ static void toJson(HttpClientOptions obj, java.util.Map<String, Object> json) {
json.put("http2ClearTextUpgrade", obj.isHttp2ClearTextUpgrade());
json.put("http2ClearTextUpgradeWithPreflightRequest", obj.isHttp2ClearTextUpgradeWithPreflightRequest());
json.put("maxRedirects", obj.getMaxRedirects());
json.put("maxRedirectBufferSize", obj.getMaxRedirectBufferSize());
json.put("forceSni", obj.isForceSni());
json.put("decoderInitialBufferSize", obj.getDecoderInitialBufferSize());
if (obj.getTracingPolicy() != null) {
Expand Down
24 changes: 24 additions & 0 deletions vertx-core/src/main/java/io/vertx/core/http/HttpClientConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
*/
package io.vertx.core.http;

import io.vertx.core.impl.Arguments;
import io.vertx.codegen.annotations.DataObject;
import io.vertx.codegen.annotations.GenIgnore;
import io.vertx.codegen.annotations.Unstable;
Expand Down Expand Up @@ -79,6 +80,7 @@ private static QuicClientConfig defaultQuicConfig() {
private String defaultHost;
private int defaultPort;
private int maxRedirects;
private int maxRedirectBufferSize;
private ObservabilityConfig observabilityConfig;
private boolean shared;
private String name;
Expand All @@ -97,6 +99,7 @@ public HttpClientConfig() {
this.defaultHost = HttpClientOptions.DEFAULT_DEFAULT_HOST;
this.defaultPort = HttpClientOptions.DEFAULT_DEFAULT_PORT;
this.maxRedirects = HttpClientOptions.DEFAULT_MAX_REDIRECTS;
this.maxRedirectBufferSize = HttpClientOptions.DEFAULT_MAX_REDIRECT_BUFFER_SIZE;
this.observabilityConfig = null;
this.shared = HttpClientOptions.DEFAULT_SHARED;
this.name = HttpClientOptions.DEFAULT_NAME;
Expand All @@ -116,6 +119,7 @@ public HttpClientConfig(HttpClientConfig other) {
this.defaultHost = other.defaultHost;
this.defaultPort = other.defaultPort;
this.maxRedirects = other.maxRedirects;
this.maxRedirectBufferSize = other.maxRedirectBufferSize;
this.observabilityConfig = other.observabilityConfig != null ? new ObservabilityConfig(other.observabilityConfig) : null;
this.shared = other.shared;
this.name = other.name;
Expand Down Expand Up @@ -143,6 +147,7 @@ public HttpClientConfig(HttpClientOptions options) {
this.defaultHost = options.getDefaultHost();
this.defaultPort = options.getDefaultPort();
this.maxRedirects = options.getMaxRedirects();
this.maxRedirectBufferSize = options.getMaxRedirectBufferSize();
this.observabilityConfig = observabilityConfig;
this.shared = options.isShared();
this.name = options.getName();
Expand Down Expand Up @@ -451,6 +456,25 @@ public HttpClientConfig setMaxRedirects(int maxRedirects) {
return this;
}

/**
* @return the maximum size in bytes of the redirect buffer when redirecting QUERY requests
*/
public int getMaxRedirectBufferSize() {
return maxRedirectBufferSize;
}

/**
* Set the maximum size of the redirect buffer in bytes when redirecting QUERY requests.
*
* @param maxRedirectBufferSize the maximum buffer size
* @return a reference to this, so the API can be used fluently
*/
public HttpClientConfig setMaxRedirectBufferSize(int maxRedirectBufferSize) {
Arguments.require(maxRedirectBufferSize >= 0, "Max redirect buffer size must be >= 0");
this.maxRedirectBufferSize = maxRedirectBufferSize;
Comment thread
desiderantes marked this conversation as resolved.
return this;
}

/**
* @return the client observability config.
*/
Expand Down
28 changes: 28 additions & 0 deletions vertx-core/src/main/java/io/vertx/core/http/HttpClientOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

package io.vertx.core.http;

import io.vertx.core.impl.Arguments;
import io.netty.handler.logging.ByteBufFormat;
import io.vertx.codegen.annotations.DataObject;
import io.vertx.codegen.json.annotations.JsonGen;
Expand Down Expand Up @@ -133,6 +134,11 @@ public class HttpClientOptions extends ClientOptionsBase {
*/
public static final int DEFAULT_MAX_REDIRECTS = 16;

/**
* Default max redirect buffer size = 4 * 1024 bytes (4KB)
*/
public static final int DEFAULT_MAX_REDIRECT_BUFFER_SIZE = 4 * 1024;

/*
* Default force SNI = {@code false}
*/
Expand Down Expand Up @@ -171,6 +177,7 @@ public class HttpClientOptions extends ClientOptionsBase {
private int defaultPort;
private HttpVersion protocolVersion;
private int maxRedirects;
private int maxRedirectBufferSize;
private boolean forceSni;

private TracingPolicy tracingPolicy;
Expand Down Expand Up @@ -211,6 +218,7 @@ public HttpClientOptions(HttpClientOptions other) {
this.defaultPort = other.defaultPort;
this.protocolVersion = other.protocolVersion;
this.maxRedirects = other.maxRedirects;
this.maxRedirectBufferSize = other.maxRedirectBufferSize;
this.forceSni = other.forceSni;
this.tracingPolicy = other.tracingPolicy;
this.shared = other.shared;
Expand Down Expand Up @@ -248,6 +256,7 @@ private void init() {
defaultPort = DEFAULT_DEFAULT_PORT;
protocolVersion = DEFAULT_PROTOCOL_VERSION;
maxRedirects = DEFAULT_MAX_REDIRECTS;
maxRedirectBufferSize = DEFAULT_MAX_REDIRECT_BUFFER_SIZE;
forceSni = DEFAULT_FORCE_SNI;
tracingPolicy = DEFAULT_TRACING_POLICY;
shared = DEFAULT_SHARED;
Expand Down Expand Up @@ -896,6 +905,25 @@ public HttpClientOptions setMaxRedirects(int maxRedirects) {
return this;
}

/**
* @return the maximum size in bytes of the redirect buffer when redirecting QUERY requests
*/
public int getMaxRedirectBufferSize() {
return maxRedirectBufferSize;
}

/**
* Set the maximum size of the redirect buffer in bytes when redirecting QUERY requests.
*
* @param maxRedirectBufferSize the maximum buffer size
* @return a reference to this, so the API can be used fluently
*/
public HttpClientOptions setMaxRedirectBufferSize(int maxRedirectBufferSize) {
Arguments.require(maxRedirectBufferSize >= 0, "Max redirect buffer size must be >= 0");
this.maxRedirectBufferSize = maxRedirectBufferSize;
return this;
}

/**
* @return whether the client should always use SNI on TLS/SSL connections
*/
Expand Down
14 changes: 14 additions & 0 deletions vertx-core/src/main/java/io/vertx/core/http/HttpClientRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,20 @@ public interface HttpClientRequest extends WriteStream<Buffer> {
*/
int getMaxRedirects();

/**
* @return the maximum size in bytes of the redirect buffer when redirecting QUERY requests
*/
int maxRedirectBufferSize();

/**
* Set the maximum size of the redirect buffer in bytes when redirecting QUERY requests.
*
* @param maxRedirectBufferSize the maximum buffer size
* @return a reference to this, so the API can be used fluently
*/
@Fluent
HttpClientRequest maxRedirectBufferSize(int maxRedirectBufferSize);

/**
* @return the number of followed redirections for the current HTTP request
*/
Expand Down
8 changes: 8 additions & 0 deletions vertx-core/src/main/java/io/vertx/core/http/HttpHeaders.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ public interface HttpHeaders {
@GenIgnore(GenIgnore.PERMITTED_TYPE)
CharSequence ACCEPT_PATCH = HttpHeaderNames.ACCEPT_PATCH;

/**
* Accept-Query header name
Comment thread
desiderantes marked this conversation as resolved.
* <p>
* TODO: Use {@code HttpHeaderNames.ACCEPT_QUERY} when bumping to the next Netty release
*/
@GenIgnore(GenIgnore.PERMITTED_TYPE)
CharSequence ACCEPT_QUERY = createOptimized("accept-query");

/**
* Access-Control-Allow-Credentials header name
*/
Expand Down
14 changes: 13 additions & 1 deletion vertx-core/src/main/java/io/vertx/core/http/HttpMethod.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ public class HttpMethod {
*/
public static final HttpMethod PATCH;

/**
* The RFC 10008 {@code QUERY} method, this instance is interned and uniquely used.
*/
public static final HttpMethod QUERY;

/**
* The RFC 2518/4918 {@code PROPFIND} method, this instance is interned and uniquely used.
*/
Expand Down Expand Up @@ -198,6 +203,8 @@ public class HttpMethod {
TRACE = new HttpMethod(io.netty.handler.codec.http.HttpMethod.TRACE);
CONNECT = new HttpMethod(io.netty.handler.codec.http.HttpMethod.CONNECT);
PATCH = new HttpMethod(io.netty.handler.codec.http.HttpMethod.PATCH);
// TODO: Use Netty HttpMethod.QUERY in the next Netty bump
QUERY = new HttpMethod(io.netty.handler.codec.http.HttpMethod.valueOf("QUERY"));
Comment thread
desiderantes marked this conversation as resolved.
PROPFIND = new HttpMethod(io.netty.handler.codec.http.HttpMethod.valueOf("PROPFIND"));
PROPPATCH = new HttpMethod(io.netty.handler.codec.http.HttpMethod.valueOf("PROPPATCH"));
MKCOL = new HttpMethod(io.netty.handler.codec.http.HttpMethod.valueOf("MKCOL"));
Expand Down Expand Up @@ -252,7 +259,8 @@ public class HttpMethod {
HttpMethod.MKACTIVITY,
HttpMethod.ORDERPATCH,
HttpMethod.ACL,
HttpMethod.SEARCH
HttpMethod.SEARCH,
HttpMethod.QUERY
));
}

Expand Down Expand Up @@ -303,6 +311,8 @@ private static HttpMethod _fromNetty(io.netty.handler.codec.http.HttpMethod sMet
return CONNECT;
case "PATCH":
return PATCH;
case "QUERY":
return QUERY;
case "PROPFIND":
return PROPFIND;
case "PROPPATCH":
Expand Down Expand Up @@ -386,6 +396,8 @@ public static HttpMethod valueOf(String value) {
return CONNECT;
case "PATCH":
return PATCH;
case "QUERY":
return QUERY;
case "PROPFIND":
return PROPFIND;
case "PROPPATCH":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public Future<RequestOptions> apply(HttpClientResponse resp) {
HttpMethod m = resp.request().getMethod();
if (statusCode == 303) {
m = HttpMethod.GET;
} else if (m != HttpMethod.GET && m != HttpMethod.HEAD) {
} else if (m != HttpMethod.GET && m != HttpMethod.HEAD && m != HttpMethod.QUERY) {
return null;
}
URI uri = HttpUtils.resolveURIReference(resp.request().absoluteURI(), location);
Expand Down Expand Up @@ -72,6 +72,7 @@ public Future<RequestOptions> apply(HttpClientResponse resp) {
options.setURI(requestURI);
options.setHeaders(resp.request().headers());
options.removeHeader(CONTENT_LENGTH);
options.removeHeader(HttpHeaders.TRANSFER_ENCODING);
return Future.succeededFuture(options);
}
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ private HttpClientImpl createHttpClientImpl(HttpClientConfig config,
config.getDefaultHost(),
config.getDefaultPort(),
config.getMaxRedirects(),
config.getMaxRedirectBufferSize(),
versions,
sslOptions,
connectHandler,
Expand Down Expand Up @@ -210,14 +211,15 @@ public LegacyHttpClient(
String defaultHost,
int defaultPort,
int maxRedirects,
int maxRedirectBufferSize,
List<HttpVersion> versions,
ClientSSLOptions sslOptions,
Handler<HttpConnection> connectHandler,
HttpClientTransport tcpTransport,
HttpClientTransport quicTransport,
HttpClientConfig config,
HttpClientOptions options) {
super(vertx, resolver, redirectHandler, httpMetrics, poolOptions, defaultProxyOptions, nonProxyHosts, loadBalancer, followAlternativeServices, resolverIdeTimeout, verifyHost, defaultSsl, defaultHost, defaultPort, maxRedirects, versions, sslOptions, connectHandler, tcpTransport, quicTransport);
super(vertx, resolver, redirectHandler, httpMetrics, poolOptions, defaultProxyOptions, nonProxyHosts, loadBalancer, followAlternativeServices, resolverIdeTimeout, verifyHost, defaultSsl, defaultHost, defaultPort, maxRedirects, maxRedirectBufferSize, versions, sslOptions, connectHandler, tcpTransport, quicTransport);
this.config = config;
this.options = options;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public class HttpClientImpl extends HttpClientBase implements HttpClientInternal
private final String defaultHost;
private final int defaultPort;
private final int maxRedirects;
private final int maxRedirectBufferSize;
private final List<HttpVersion> versions;
private final Handler<HttpConnection> connectHandler;
private volatile Handler<Throwable> exceptionHandler;
Expand All @@ -89,6 +90,7 @@ public class HttpClientImpl extends HttpClientBase implements HttpClientInternal
String defaultHost,
int defaultPort,
int maxRedirects,
int maxRedirectBufferSize,
List<HttpVersion> versions,
ClientSSLOptions sslOptions,
Handler<HttpConnection> connectHandler,
Expand Down Expand Up @@ -117,6 +119,7 @@ public class HttpClientImpl extends HttpClientBase implements HttpClientInternal
this.defaultHost = defaultHost;
this.defaultPort = defaultPort;
this.maxRedirects = maxRedirects;
this.maxRedirectBufferSize = maxRedirectBufferSize;
this.versions = versions;
this.sslOptions = sslOptions;
this.connectHandler = connectHandler;
Expand Down Expand Up @@ -791,6 +794,7 @@ public ConnectionObtainedResult(HttpClientStream stream, Lease<HttpClientConnect
HttpClientRequestImpl createRequest(HostAndPort authority, HttpConnection connection, HttpClientStream stream, RequestOptions options) {
HttpClientRequestImpl request = new HttpClientRequestImpl(authority, connection, stream);
request.init(options);
request.maxRedirectBufferSize(maxRedirectBufferSize);
Function<HttpClientResponse, Future<RequestOptions>> rHandler = redirectHandler;
if (rHandler != null) {
request.setMaxRedirects(maxRedirects);
Expand Down
Loading
Loading