Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace HTTP client host/port properties by an authority property #4778

Merged
merged 2 commits into from
Jul 12, 2023
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
4 changes: 2 additions & 2 deletions src/main/asciidoc/http.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,9 @@ It also has case-insensitive keys, that means you can do the following:
{@link examples.HTTPExamples#example8}
----

==== Request host
==== Request authority

Use {@link io.vertx.core.http.HttpServerRequest#host} to return the host of the HTTP request.
Use {@link io.vertx.core.http.HttpServerRequest#authority} to return the authority of the HTTP request.

For HTTP/1.x requests the `host` header is returned, for HTTP/1 requests the `:authority` pseudo header is returned.

Expand Down
32 changes: 5 additions & 27 deletions src/main/java/io/vertx/core/http/HttpClientRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import io.vertx.core.Handler;
import io.vertx.core.MultiMap;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.net.HostAndPort;
import io.vertx.core.net.NetSocket;
import io.vertx.core.streams.ReadStream;
import io.vertx.core.streams.WriteStream;
Expand Down Expand Up @@ -63,37 +64,14 @@ public interface HttpClientRequest extends WriteStream<Buffer> {
HttpClientRequest drainHandler(Handler<Void> handler);

/**
* Set the host value of the HTTP/1.1 {@code host} header or HTTP/2 {@code authority} pseudo header
* <p>The initial value is the same than the server socket address host.
* <p>Keep in mind that changing this value won't change the actual server socket address for this request.
* Set the request authority, when using HTTP/1.x this overrides the request {@code host} header, when using
* HTTP/2 this sets the {@code authority} pseudo header.
*
* @param host the host part of the HTTP/1.1 {@code host} header or HTTP/2 {@code authority} pseudo header
* @param authority the authority
* @return a reference to this, so the API can be used fluently
*/
@Fluent
HttpClientRequest setHost(String host);

/**
* @return the host value of the HTTP/1.1 {@code host} header or HTTP/2 {@code authority} pseudo header
*/
String getHost();

/**
* Set the port value of the HTTP/1.1 {@code host} header or HTTP/2 {@code authority} pseudo header
*
* <p> Keep in mind that this won't change the actual server socket address for this request.
* <p>The initial value is the same than the server socket address port.
*
* @param port the port part of the HTTP/1.1 {@code host} header or HTTP/2 {@code authority} pseudo header
* @return a reference to this, so the API can be used fluently
*/
@Fluent
HttpClientRequest setPort(int port);

/**
* @return the port value of the HTTP/1.1 {@code host} header or HTTP/2 {@code authority} pseudo header
*/
int getPort();
HttpClientRequest authority(HostAndPort authority);

/**
* Set the request to follow HTTP redirects up to {@link HttpClientOptions#getMaxRedirects()}.
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/io/vertx/core/http/HttpServerRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import io.vertx.core.Handler;
import io.vertx.core.MultiMap;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.net.HostAndPort;
import io.vertx.core.net.NetSocket;
import io.vertx.core.net.SocketAddress;
import io.vertx.core.streams.ReadStream;
Expand Down Expand Up @@ -135,10 +136,10 @@ default boolean isSSL() {
String query();

/**
* @return the request host. For HTTP2 it returns the {@literal :authority} pseudo header otherwise it returns the {@literal Host} header
* @return the request authority. For HTTP2 it returns the {@literal :authority} pseudo header otherwise it returns the {@literal Host} header
*/
@Nullable
String host();
HostAndPort authority();

/**
* @return the total number of bytes read for the body of the request.
Expand Down
24 changes: 23 additions & 1 deletion src/main/java/io/vertx/core/http/HttpServerResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import io.vertx.core.Handler;
import io.vertx.core.MultiMap;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.net.HostAndPort;
import io.vertx.core.streams.ReadStream;
import io.vertx.core.streams.WriteStream;

Expand Down Expand Up @@ -421,7 +422,7 @@ default Future<HttpServerResponse> push(HttpMethod method, String host, String p
* Like {@link #push(HttpMethod, String, String, MultiMap)} with the host copied from the current request.
*/
default Future<HttpServerResponse> push(HttpMethod method, String path, MultiMap headers) {
return push(method, null, path, headers);
return push(method, (HostAndPort) null, path, headers);
}

/**
Expand All @@ -431,6 +432,25 @@ default Future<HttpServerResponse> push(HttpMethod method, String path) {
return push(method, null, path);
}

/**
* Push a response to the client.<p/>
*
* The {@code handler} will be notified with a <i>success</i> when the push can be sent and with
* a <i>failure</i> when the client has disabled push or reset the push before it has been sent.<p/>
*
* The {@code handler} may be queued if the client has reduced the maximum number of streams the server can push
* concurrently.<p/>
*
* Push can be sent only for peer initiated streams and if the response is not ended.
*
* @param method the method of the promised request
* @param authority the authority of the promised request
* @param path the path of the promised request
* @param headers the headers of the promised request
* @return a future notified when the response can be written
*/
Future<HttpServerResponse> push(HttpMethod method, HostAndPort authority, String path, MultiMap headers);

/**
* Push a response to the client.<p/>
*
Expand All @@ -447,7 +467,9 @@ default Future<HttpServerResponse> push(HttpMethod method, String path) {
* @param path the path of the promised request
* @param headers the headers of the promised request
* @return a future notified when the response can be written
* @deprecated instead use {@link #push(HttpMethod, HostAndPort, String, MultiMap)}
*/
@Deprecated
Future<HttpServerResponse> push(HttpMethod method, String host, String path, MultiMap headers);

/**
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/io/vertx/core/http/ServerWebSocket.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import io.vertx.core.Handler;
import io.vertx.core.Promise;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.net.HostAndPort;

import javax.net.ssl.SSLSession;

Expand Down Expand Up @@ -69,10 +70,10 @@ public interface ServerWebSocket extends WebSocketBase {
String scheme();

/**
* @return the WebSocket handshake host
* @return the WebSocket handshake authority
*/
@Nullable
String host();
HostAndPort authority();

/*
* @return the WebSocket handshake URI. This is a relative URI.
Expand Down
12 changes: 9 additions & 3 deletions src/main/java/io/vertx/core/http/impl/Http1xServerRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import io.netty.handler.codec.http.multipart.Attribute;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
import io.netty.handler.codec.http.multipart.InterfaceHttpData;
import io.vertx.codegen.annotations.Nullable;
import io.vertx.core.Context;
import io.vertx.core.Future;
import io.vertx.core.Handler;
Expand All @@ -32,6 +31,8 @@
import io.vertx.core.impl.future.PromiseInternal;
import io.vertx.core.impl.logging.Logger;
import io.vertx.core.impl.logging.LoggerFactory;
import io.vertx.core.net.HostAndPort;
import io.vertx.core.net.impl.HostAndPortImpl;
import io.vertx.core.net.NetSocket;
import io.vertx.core.net.SocketAddress;
import io.vertx.core.spi.metrics.HttpServerMetrics;
Expand Down Expand Up @@ -71,6 +72,7 @@ public class Http1xServerRequest extends HttpServerRequestInternal implements io
private HttpRequest request;
private io.vertx.core.http.HttpVersion version;
private io.vertx.core.http.HttpMethod method;
private HostAndPort authority;
private String uri;
private String path;
private String query;
Expand Down Expand Up @@ -252,8 +254,12 @@ public String query() {
}

@Override
public @Nullable String host() {
return getHeader(HttpHeaderNames.HOST);
public synchronized HostAndPort authority() {
if (authority == null) {
String host = getHeader(HttpHeaderNames.HOST);
authority = HostAndPortImpl.parseHostAndPort(host, isSSL() ? 443 : 80);
}
return authority;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import io.vertx.core.impl.future.PromiseInternal;
import io.vertx.core.impl.logging.Logger;
import io.vertx.core.impl.logging.LoggerFactory;
import io.vertx.core.net.HostAndPort;
import io.vertx.core.net.NetSocket;
import io.vertx.core.spi.metrics.Metrics;
import io.vertx.core.spi.observability.HttpResponse;
Expand Down Expand Up @@ -733,6 +734,11 @@ public boolean reset(long code) {
return true;
}

@Override
public Future<HttpServerResponse> push(HttpMethod method, HostAndPort authority, String path, MultiMap headers) {
return context.failedFuture("HTTP/1 does not support response push");
}

@Override
public Future<HttpServerResponse> push(HttpMethod method, String host, String path, MultiMap headers) {
return context.failedFuture("HTTP/1 does not support response push");
Expand Down
29 changes: 22 additions & 7 deletions src/main/java/io/vertx/core/http/impl/Http2ServerConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import io.vertx.core.http.*;
import io.vertx.core.impl.ContextInternal;
import io.vertx.core.impl.EventLoopContext;
import io.vertx.core.net.HostAndPort;
import io.vertx.core.spi.metrics.HttpServerMetrics;

import java.net.URI;
Expand Down Expand Up @@ -109,6 +110,18 @@ private static boolean isMalformedRequest(Http2Headers headers) {
if (uri.getRawUserInfo() != null) {
return true;
}
CharSequence host = headers.get(HttpHeaders.HOST);
if (host != null) {
URI hostURI;
try {
hostURI = new URI(null, host.toString(), null, null, null);
} catch (URISyntaxException e) {
return true;
}
if (uri.getRawUserInfo() != null || !uri.getAuthority().equals(hostURI.getAuthority())) {
return true;
}
}
}
return false;
}
Expand Down Expand Up @@ -176,22 +189,24 @@ protected synchronized void onHeadersRead(int streamId, Http2Headers headers, St
}
}

void sendPush(int streamId, String host, HttpMethod method, MultiMap headers, String path, StreamPriority streamPriority, Promise<HttpServerResponse> promise) {
void sendPush(int streamId, HostAndPort authority, HttpMethod method, MultiMap headers, String path, StreamPriority streamPriority, Promise<HttpServerResponse> promise) {
EventLoop eventLoop = context.nettyEventLoop();
if (eventLoop.inEventLoop()) {
doSendPush(streamId, host, method, headers, path, streamPriority, promise);
doSendPush(streamId, authority, method, headers, path, streamPriority, promise);
} else {
eventLoop.execute(() -> doSendPush(streamId, host, method, headers, path, streamPriority, promise));
eventLoop.execute(() -> doSendPush(streamId, authority, method, headers, path, streamPriority, promise));
}
}

private synchronized void doSendPush(int streamId, String host, HttpMethod method, MultiMap headers, String path, StreamPriority streamPriority, Promise<HttpServerResponse> promise) {
private synchronized void doSendPush(int streamId, HostAndPort authority, HttpMethod method, MultiMap headers, String path, StreamPriority streamPriority, Promise<HttpServerResponse> promise) {
boolean ssl = isSsl();
Http2Headers headers_ = new DefaultHttp2Headers();
headers_.method(method.name());
headers_.path(path);
headers_.scheme(isSsl() ? "https" : "http");
if (host != null) {
headers_.authority(host);
headers_.scheme(ssl ? "https" : "http");
if (authority != null) {
String s = (ssl && authority.port() == 443) || (!ssl && authority.port() == 80) ? authority.host() : authority.host() + ':' + authority.port();
headers_.authority(s);
}
if (headers != null) {
headers.forEach(header -> headers_.add(header.getKey(), header.getValue()));
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/io/vertx/core/http/impl/Http2ServerRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@
import io.vertx.core.impl.ContextInternal;
import io.vertx.core.impl.logging.Logger;
import io.vertx.core.impl.logging.LoggerFactory;
import io.vertx.core.net.HostAndPort;
import io.vertx.core.net.NetSocket;
import io.vertx.core.net.SocketAddress;
import io.vertx.core.tracing.TracingPolicy;

import javax.net.ssl.SSLPeerUnverifiedException;
import javax.security.cert.X509Certificate;
Expand Down Expand Up @@ -334,8 +334,8 @@ public String scheme() {
}

@Override
public String host() {
return stream.host;
public @Nullable HostAndPort authority() {
return stream.authority;
}

@Override
Expand Down
30 changes: 26 additions & 4 deletions src/main/java/io/vertx/core/http/impl/Http2ServerResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@
import io.vertx.core.http.StreamResetException;
import io.vertx.core.http.impl.headers.Http2HeadersAdaptor;
import io.vertx.core.impl.future.PromiseInternal;
import io.vertx.core.net.HostAndPort;
import io.vertx.core.net.NetSocket;
import io.vertx.core.net.impl.HostAndPortImpl;
import io.vertx.core.spi.observability.HttpResponse;
import io.vertx.core.streams.ReadStream;

Expand Down Expand Up @@ -614,18 +616,38 @@ public boolean reset(long code) {
}

@Override
public Future<HttpServerResponse> push(HttpMethod method, String host, String path, MultiMap headers) {
public Future<HttpServerResponse> push(HttpMethod method, HostAndPort authority, String path, MultiMap headers) {
if (push) {
throw new IllegalStateException("A push response cannot promise another push");
}
if (host == null) {
host = stream.host;
if (authority == null) {
authority = stream.authority;
}
synchronized (conn) {
checkValid();
}
Promise<HttpServerResponse> promise = stream.context.promise();
conn.sendPush(stream.id(), host, method, headers, path, stream.priority(), promise);
conn.sendPush(stream.id(), authority, method, headers, path, stream.priority(), promise);
return promise.future();
}

@Override
public Future<HttpServerResponse> push(HttpMethod method, String authority, String path, MultiMap headers) {
if (push) {
throw new IllegalStateException("A push response cannot promise another push");
}
HostAndPort hostAndPort = null;
if (authority != null) {
hostAndPort = HostAndPortImpl.parseHostAndPort(authority, conn.isSsl() ? 443 : 80);
}
if (hostAndPort == null) {
hostAndPort = stream.authority;
}
synchronized (conn) {
checkValid();
}
Promise<HttpServerResponse> promise = stream.context.promise();
conn.sendPush(stream.id(), hostAndPort, method, headers, path, stream.priority(), promise);
return promise.future();
}

Expand Down
8 changes: 5 additions & 3 deletions src/main/java/io/vertx/core/http/impl/Http2ServerStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import io.vertx.core.http.StreamPriority;
import io.vertx.core.http.impl.headers.Http2HeadersAdaptor;
import io.vertx.core.impl.ContextInternal;
import io.vertx.core.net.HostAndPort;
import io.vertx.core.net.impl.HostAndPortImpl;
import io.vertx.core.spi.metrics.HttpServerMetrics;
import io.vertx.core.spi.metrics.Metrics;
import io.vertx.core.spi.observability.HttpRequest;
Expand All @@ -36,7 +38,7 @@ class Http2ServerStream extends VertxHttp2Stream<Http2ServerConnection> {
protected final Http2Headers headers;
protected final HttpMethod method;
protected final String uri;
protected final String host;
protected final HostAndPort authority;
private final TracingPolicy tracingPolicy;
private Object metric;
private Object trace;
Expand All @@ -56,7 +58,7 @@ class Http2ServerStream extends VertxHttp2Stream<Http2ServerConnection> {
this.headers = null;
this.method = method;
this.uri = uri;
this.host = null;
this.authority = null;
this.tracingPolicy = tracingPolicy;
this.halfClosedRemote = halfClosedRemote;
}
Expand All @@ -71,7 +73,7 @@ class Http2ServerStream extends VertxHttp2Stream<Http2ServerConnection> {
}

this.headers = headers;
this.host = host;
this.authority = HostAndPortImpl.parseHostAndPort(host, conn.isSsl() ? 443 : 80);
this.uri = headers.get(":path") != null ? headers.get(":path").toString() : null;
this.method = headers.get(":method") != null ? HttpMethod.valueOf(headers.get(":method").toString()) : null;
this.tracingPolicy = tracingPolicy;
Expand Down
Loading
Loading