io.vertx.core.http.RequestOptions Maven / Gradle / Ivy
/*
* Copyright (c) 2011-2019 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
* which is available at https://www.apache.org/licenses/LICENSE-2.0.
*
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
*/
package io.vertx.core.http;
import io.vertx.codegen.annotations.DataObject;
import io.vertx.codegen.annotations.GenIgnore;
import io.vertx.codegen.json.annotations.JsonGen;
import io.vertx.core.MultiMap;
import io.vertx.core.VertxException;
import io.vertx.core.json.JsonObject;
import io.vertx.core.net.ProxyOptions;
import io.vertx.core.net.SocketAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static io.vertx.codegen.annotations.GenIgnore.PERMITTED_TYPE;
/**
* Options describing how an {@link HttpClient} will connect to make a request.
*
* @author Julien Viet
*/
@DataObject
@JsonGen(publicConverter = false)
public class RequestOptions {
/**
* The default value for proxy options = {@code null}
*/
public static final ProxyOptions DEFAULT_PROXY_OPTIONS = null;
/**
* The default value for server method = {@code null}
*/
public static final SocketAddress DEFAULT_SERVER = null;
/**
* The default value for HTTP method = {@link HttpMethod#GET}
*/
public static final HttpMethod DEFAULT_HTTP_METHOD = HttpMethod.GET;
/**
* The default value for host name = {@code null}
*/
public static final String DEFAULT_HOST = null;
/**
* The default value for port = {@code null}
*/
public static final Integer DEFAULT_PORT = null;
/**
* The default value for SSL = {@code null}
*/
public static final Boolean DEFAULT_SSL = null;
/**
* The default request URI = {@code "/"}
*/
public static final String DEFAULT_URI = "/";
/**
* Follow redirection by default = {@code false}
*/
public static final boolean DEFAULT_FOLLOW_REDIRECTS = false;
/**
* The default request timeout = {@code -1L} (disabled)
*/
public static final long DEFAULT_TIMEOUT = -1L;
/**
* The default connect timeout = {@code -1L} (disabled)
*/
public static final long DEFAULT_CONNECT_TIMEOUT = -1L;
/**
* The default idle timeout = {@code -1L} (disabled)
*/
public static final long DEFAULT_IDLE_TIMEOUT = -1L;
private ProxyOptions proxyOptions;
private SocketAddress server;
private HttpMethod method;
private String host;
private Integer port;
private Boolean ssl;
private String uri;
private MultiMap headers;
private boolean followRedirects;
private long timeout;
private long connectTimeout;
private long idleTimeout;
private String traceOperation;
/**
* Default constructor
*/
public RequestOptions() {
proxyOptions = DEFAULT_PROXY_OPTIONS;
server = DEFAULT_SERVER;
method = DEFAULT_HTTP_METHOD;
host = DEFAULT_HOST;
port = DEFAULT_PORT;
ssl = DEFAULT_SSL;
uri = DEFAULT_URI;
followRedirects = DEFAULT_FOLLOW_REDIRECTS;
timeout = DEFAULT_TIMEOUT;
connectTimeout = DEFAULT_CONNECT_TIMEOUT;
idleTimeout = DEFAULT_IDLE_TIMEOUT;
traceOperation = null;
}
/**
* Copy constructor
*
* @param other the options to copy
*/
public RequestOptions(RequestOptions other) {
setProxyOptions(other.proxyOptions);
setServer(other.server);
setMethod(other.method);
setHost(other.host);
setPort(other.port);
setSsl(other.ssl);
setURI(other.uri);
setFollowRedirects(other.followRedirects);
setTimeout(other.timeout);
setIdleTimeout(other.idleTimeout);
setConnectTimeout(other.connectTimeout);
if (other.headers != null) {
setHeaders(MultiMap.caseInsensitiveMultiMap().setAll(other.headers));
}
setTraceOperation(other.traceOperation);
}
/**
* Create options from JSON
*
* @param json the JSON
*/
public RequestOptions(JsonObject json) {
this();
RequestOptionsConverter.fromJson(json, this);
String method = json.getString("method");
if (method != null) {
setMethod(HttpMethod.valueOf(method));
}
JsonObject server = json.getJsonObject("server");
if (server != null) {
Integer port = server.getInteger("port", 80);
String host = server.getString("host");
String path = server.getString("path");
if (host != null) {
this.server = SocketAddress.inetSocketAddress(port, host);
} else if (path != null) {
this.server = SocketAddress.domainSocketAddress(path);
}
}
JsonObject headers = json.getJsonObject("headers");
if (headers != null) {
for (Map.Entry entry : headers) {
Object value = entry.getValue();
if (value instanceof String) {
this.addHeader(entry.getKey(), (String) value);
} else if (value instanceof Iterable) {
for (Object subValue : ((Iterable>) value)) {
if (subValue instanceof String) {
this.addHeader(entry.getKey(), (String) subValue);
}
}
}
}
}
}
/**
* Get the proxy options override for connections
*
* @return proxy options override
*/
public ProxyOptions getProxyOptions() {
return proxyOptions;
}
/**
* Override the {@link HttpClientOptions#setProxyOptions(ProxyOptions)} proxy options
* for connections.
*
* @param proxyOptions proxy options override object
* @return a reference to this, so the API can be used fluently
*/
public RequestOptions setProxyOptions(ProxyOptions proxyOptions) {
this.proxyOptions = proxyOptions;
return this;
}
/**
* Get the server address to be used by the client request.
*
* @return the server address
*/
public SocketAddress getServer() {
return server;
}
/**
* Set the server address to be used by the client request.
*
* When the server address is {@code null}, the address will be resolved after the {@code host}
* property by the Vert.x resolver.
*
*
Use this when you want to connect to a specific server address without name resolution.
*
* @return a reference to this, so the API can be used fluently
*/
public RequestOptions setServer(SocketAddress server) {
this.server = server;
return this;
}
/**
* Get the HTTP method to be used by the client request.
*
* @return the HTTP method
*/
@GenIgnore
public HttpMethod getMethod() {
return method;
}
/**
* Set the HTTP method to be used by the client request.
*
* @return a reference to this, so the API can be used fluently
*/
@GenIgnore
public RequestOptions setMethod(HttpMethod method) {
this.method = method;
return this;
}
/**
* Get the host name to be used by the client request.
*
* @return the host name
*/
public String getHost() {
return host;
}
/**
* Set the host name to be used by the client request.
*
* @return a reference to this, so the API can be used fluently
*/
public RequestOptions setHost(String host) {
this.host = host;
return this;
}
/**
* Get the port to be used by the client request.
*
* @return the port
*/
public Integer getPort() {
return port;
}
/**
* Set the port to be used by the client request.
*
* @return a reference to this, so the API can be used fluently
*/
public RequestOptions setPort(Integer port) {
this.port = port;
return this;
}
/**
* @return is SSL/TLS enabled?
*/
public Boolean isSsl() {
return ssl;
}
/**
* Set whether SSL/TLS is enabled.
*
* @param ssl true if enabled
* @return a reference to this, so the API can be used fluently
*/
public RequestOptions setSsl(Boolean ssl) {
this.ssl = ssl;
return this;
}
/**
* @return the request relative URI
*/
public String getURI() {
return uri;
}
/**
* Set the request relative URI.
*
* @param uri the relative uri
* @return a reference to this, so the API can be used fluently
*/
public RequestOptions setURI(String uri) {
this.uri = uri;
return this;
}
/**
* @return {@code true} when the client should follow redirection
*/
public Boolean getFollowRedirects() {
return followRedirects;
}
/**
* Set whether to follow HTTP redirect
*
* @param followRedirects whether to follow redirect
* @return a reference to this, so the API can be used fluently
*/
public RequestOptions setFollowRedirects(Boolean followRedirects) {
this.followRedirects = followRedirects;
return this;
}
/**
* @see #setTimeout(long)
*/
public long getTimeout() {
return timeout;
}
/**
* Sets both connect and idle timeouts for the request
*
*
* - connect timeout: if the request is not obtained from the client within the timeout period, the {@code Future
}
* obtained from the client is failed with a {@link java.util.concurrent.TimeoutException}.
* - idle timeout: if the request does not return any data within the timeout period, the request/response is closed and the
* related futures are failed with a {@link java.util.concurrent.TimeoutException}, e.g. {@code Future
}
* or {@code Future} response body.
*
*
* The connect and idle timeouts can be set separately using {@link #setConnectTimeout(long)} and {@link #setIdleTimeout(long)}
*/
public RequestOptions setTimeout(long timeout) {
this.timeout = timeout;
return this;
}
/**
* @return the amount of time after which, if the request is not obtained from the client within the timeout period,
* the {@code Future} obtained from the client is failed with a {@link java.util.concurrent.TimeoutException}
*/
public long getConnectTimeout() {
return connectTimeout;
}
/**
* Sets the amount of time after which, if the request is not obtained from the client within the timeout period,
* the {@code Future} obtained from the client is failed with a {@link java.util.concurrent.TimeoutException}.
*
* Note this is not related to the TCP {@link HttpClientOptions#setConnectTimeout(int)} option, when a request is made against
* a pooled HTTP client, the timeout applies to the duration to obtain a connection from the pool to serve the request, the timeout
* might fire because the server does not respond in time or the pool is too busy to serve a request.
*
* @param timeout the amount of time in milliseconds.
* @return a reference to this, so the API can be used fluently
*/
public RequestOptions setConnectTimeout(long timeout) {
this.connectTimeout = timeout;
return this;
}
/**
* @return the amount of time after which, if the request does not return any data within the timeout period,
* the request/response is closed and the related futures are failed with a {@link java.util.concurrent.TimeoutException}
*/
public long getIdleTimeout() {
return idleTimeout;
}
/**
* Sets the amount of time after which, if the request does not return any data within the timeout period,
* the request/response is closed and the related futures are failed with a {@link java.util.concurrent.TimeoutException},
* e.g. {@code Future} or {@code Future} response body.
*
* The timeout starts after a connection is obtained from the client, similar to calling
* {@link HttpClientRequest#idleTimeout(long)}.
*
* @param timeout the amount of time in milliseconds.
* @return a reference to this, so the API can be used fluently
*/
public RequestOptions setIdleTimeout(long timeout) {
this.idleTimeout = timeout;
return this;
}
private URL parseUrl(String surl) {
// Note - parsing a URL this way is slower than specifying host, port and relativeURI
try {
return new URL(surl);
} catch (MalformedURLException e) {
throw new VertxException("Invalid url: " + surl, e);
}
}
/**
* Parse an absolute URI to use, this will update the {@code ssl}, {@code host},
* {@code port} and {@code uri} fields.
*
* @param absoluteURI the uri to use
* @return a reference to this, so the API can be used fluently
*/
public RequestOptions setAbsoluteURI(String absoluteURI) {
Objects.requireNonNull(absoluteURI, "Cannot set a null absolute URI");
URL url = parseUrl(absoluteURI);
return setAbsoluteURI(url);
}
/**
* Like {@link #setAbsoluteURI(String)} but using an {@link URL} parameter.
*
* @param url the uri to use
* @return a reference to this, so the API can be used fluently
*/
@GenIgnore(PERMITTED_TYPE)
public RequestOptions setAbsoluteURI(URL url) {
Objects.requireNonNull(url, "Cannot set a null absolute URI");
Boolean ssl = Boolean.FALSE;
int port = url.getPort();
String relativeUri = url.getPath().isEmpty() ? "/" + url.getFile() : url.getFile();
String protocol = url.getProtocol();
switch (protocol) {
case "http":
if (port == -1) {
port = 80;
}
break;
case "https": {
ssl = Boolean.TRUE;
if (port == -1) {
port = 443;
}
break;
}
default:
throw new IllegalArgumentException();
}
this.uri = relativeUri;
this.port = port;
this.ssl = ssl;
this.host = url.getHost();
return this;
}
/**
* Add a request header.
*
* @param key the header key
* @param value the header value
* @return a reference to this, so the API can be used fluently
*/
@GenIgnore
public RequestOptions addHeader(String key, String value) {
return addHeader((CharSequence) key, value);
}
/**
* Add a request header.
*
* @param key the header key
* @param value the header value
* @return a reference to this, so the API can be used fluently
*/
@GenIgnore
public RequestOptions addHeader(CharSequence key, CharSequence value) {
checkHeaders();
Objects.requireNonNull(key, "no null key accepted");
Objects.requireNonNull(value, "no null value accepted");
headers.add(key, value);
return this;
}
@GenIgnore
public RequestOptions addHeader(CharSequence key, Iterable values) {
checkHeaders();
Objects.requireNonNull(key, "no null key accepted");
Objects.requireNonNull(values, "no null values accepted");
headers.add(key, values);
return this;
}
/**
* Set a request header.
*
* @param key the header key
* @param value the header value
* @return a reference to this, so the API can be used fluently
*/
@GenIgnore
public RequestOptions putHeader(String key, String value) {
return putHeader((CharSequence) key, value);
}
/**
* Set a request header.
*
* @param key the header key
* @param value the header value
* @return a reference to this, so the API can be used fluently
*/
@GenIgnore
public RequestOptions putHeader(CharSequence key, CharSequence value) {
checkHeaders();
headers.set(key, value);
return this;
}
@GenIgnore
public RequestOptions putHeader(CharSequence key, Iterable values) {
checkHeaders();
headers.set(key, values);
return this;
}
/**
* Add a request header.
*
* @param key the header key
* @return a reference to this, so the API can be used fluently
*/
@GenIgnore
public RequestOptions removeHeader(String key) {
return removeHeader((CharSequence) key);
}
/**
* Add a request header.
*
* @param key the header key
* @return a reference to this, so the API can be used fluently
*/
@GenIgnore
public RequestOptions removeHeader(CharSequence key) {
if (headers != null) {
headers.remove(key);
}
return this;
}
/**
* Set request headers from a multi-map.
*
* @param headers the headers
* @return a reference to this, so the API can be used fluently
*/
@GenIgnore
public RequestOptions setHeaders(MultiMap headers) {
this.headers = headers;
return this;
}
/**
* Get the request headers
*
* @return the headers
*/
@GenIgnore
public MultiMap getHeaders() {
return headers;
}
private void checkHeaders() {
if (headers == null) {
headers = HttpHeaders.headers();
}
}
/**
* @return the trace operation override
*/
public String getTraceOperation() {
return traceOperation;
}
/**
* Override the operation the tracer use for this request. When no operation is set, the HTTP method is used
* instead.
*
* @param op the override
* @return a reference to this, so the API can be used fluently
*/
public RequestOptions setTraceOperation(String op) {
this.traceOperation = op;
return this;
}
public JsonObject toJson() {
JsonObject json = new JsonObject();
RequestOptionsConverter.toJson(this, json);
if (method != null) {
json.put("method", method.name());
}
if (this.server != null) {
JsonObject server = new JsonObject();
if (this.server.isInetSocket()) {
server.put("host", this.server.host());
server.put("port", this.server.port());
} else {
server.put("path", this.server.path());
}
json.put("server", server);
}
if (this.headers != null) {
JsonObject headers = new JsonObject();
for (String name : this.headers.names()) {
List values = this.headers.getAll(name);
if (values.size() == 1) {
headers.put(name, values.iterator().next());
} else {
headers.put(name, values);
}
}
json.put("headers", headers);
}
return json;
}
}