Skip to content

Commit 02f9ae6

Browse files
committed
Refactor UriEndpoint (#2700)
Clean up UriEndpoint logic to be based around the java.net.URI. Move relevant methods into this class. Construct instances using static method. Only calculate derived values on demand (e.g. toExternalForm) when needed in the various request implementations. Cleaned up redirection logic. Added additional tests. Made conversion from InetSocketAddress more efficient (removed the substrings which trimmed out the ports). Fixed HttpClientTest to support ipv6 (by using NetUtil.toSocketAddressString to get the [] around the address, and also fixed FailedHttpClientRequest.uri() from returning full URI rather than just Netty definition of "uri" which is raw path and query. Fixed ClientTransportTest to support ipv6. Fixes #829
1 parent 940d203 commit 02f9ae6

File tree

9 files changed

+447
-375
lines changed

9 files changed

+447
-375
lines changed

Diff for: reactor-netty-http/src/main/java/reactor/netty/http/client/AbstractHttpClientMetricsHandler.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021-2022 VMware, Inc. or its affiliates, All Rights Reserved.
2+
* Copyright (c) 2021-2023 VMware, Inc. or its affiliates, All Rights Reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -163,7 +163,7 @@ private void extractDetailsFromHttpRequest(ChannelHandlerContext ctx, HttpReques
163163
ChannelOperations<?, ?> channelOps = ChannelOperations.get(ctx.channel());
164164
if (channelOps instanceof HttpClientOperations) {
165165
HttpClientOperations ops = (HttpClientOperations) channelOps;
166-
path = uriTagValue == null ? ops.path : uriTagValue.apply(ops.path);
166+
path = uriTagValue == null ? ops.fullPath() : uriTagValue.apply(ops.fullPath());
167167
contextView = ops.currentContextView();
168168
}
169169

Diff for: reactor-netty-http/src/main/java/reactor/netty/http/client/FailedHttpClientRequest.java

+6-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020-2021 VMware, Inc. or its affiliates, All Rights Reserved.
2+
* Copyright (c) 2020-2023 VMware, Inc. or its affiliates, All Rights Reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -23,7 +23,6 @@
2323
import io.netty.handler.codec.http.cookie.ClientCookieDecoder;
2424
import io.netty.handler.codec.http.cookie.Cookie;
2525
import reactor.netty.http.Cookies;
26-
import reactor.netty.http.HttpOperations;
2726
import reactor.util.context.Context;
2827
import reactor.util.context.ContextView;
2928

@@ -45,18 +44,16 @@ final class FailedHttpClientRequest implements HttpClientRequest {
4544
final HttpHeaders headers;
4645
final boolean isWebsocket;
4746
final HttpMethod method;
48-
final String path;
4947
final Duration responseTimeout;
50-
final String uri;
48+
final UriEndpoint uriEndpoint;
5149

5250
FailedHttpClientRequest(ContextView contextView, HttpClientConfig c) {
5351
this.contextView = contextView;
5452
this.cookieDecoder = c.cookieDecoder;
5553
this.headers = c.headers;
5654
this.isWebsocket = c.websocketClientSpec != null;
5755
this.method = c.method;
58-
this.uri = c.uri == null ? c.uriStr : c.uri.toString();
59-
this.path = this.uri != null ? HttpOperations.resolvePath(this.uri) : null;
56+
this.uriEndpoint = UriEndpoint.create(c.uri, c.baseUrl, c.uriStr, c.remoteAddress(), c.isSecure(), c.websocketClientSpec != null);
6057
this.responseTimeout = c.responseTimeout;
6158
}
6259

@@ -89,7 +86,7 @@ public ContextView currentContextView() {
8986

9087
@Override
9188
public String fullPath() {
92-
return path;
89+
return uriEndpoint.getPath();
9390
}
9491

9592
@Override
@@ -144,12 +141,12 @@ public HttpClientRequest responseTimeout(Duration maxReadOperationInterval) {
144141

145142
@Override
146143
public String resourceUrl() {
147-
return null;
144+
return uriEndpoint.toExternalForm();
148145
}
149146

150147
@Override
151148
public String uri() {
152-
return uri;
149+
return uriEndpoint.getRawUri();
153150
}
154151

155152
@Override

Diff for: reactor-netty-http/src/main/java/reactor/netty/http/client/HttpClient.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2011-2022 VMware, Inc. or its affiliates, All Rights Reserved.
2+
* Copyright (c) 2011-2023 VMware, Inc. or its affiliates, All Rights Reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -1608,4 +1608,8 @@ static String reactorNettyVersion() {
16081608
static final String WS_SCHEME = "ws";
16091609

16101610
static final String WSS_SCHEME = "wss";
1611+
1612+
static final int DEFAULT_PORT = 80;
1613+
1614+
static final int DEFAULT_SECURE_PORT = 443;
16111615
}

Diff for: reactor-netty-http/src/main/java/reactor/netty/http/client/HttpClientConnect.java

+8-76
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@
1717

1818
import java.net.InetSocketAddress;
1919
import java.net.SocketAddress;
20-
import java.net.URI;
21-
import java.net.URISyntaxException;
2220
import java.util.ArrayList;
2321
import java.util.Arrays;
2422
import java.util.Collections;
@@ -38,7 +36,6 @@
3836
import io.netty.handler.codec.http.HttpHeaders;
3937
import io.netty.handler.codec.http.HttpMethod;
4038
import io.netty.handler.codec.http.HttpResponseStatus;
41-
import io.netty.handler.codec.http.HttpUtil;
4239
import io.netty.handler.codec.http.HttpVersion;
4340
import io.netty.handler.ssl.SslClosedEngineException;
4441
import io.netty.resolver.AddressResolverGroup;
@@ -55,7 +52,6 @@
5552
import reactor.netty.ConnectionObserver;
5653
import reactor.netty.NettyOutbound;
5754
import reactor.netty.channel.AbortedException;
58-
import reactor.netty.http.HttpOperations;
5955
import reactor.netty.http.HttpProtocol;
6056
import reactor.netty.tcp.TcpClientConfig;
6157
import reactor.netty.transport.AddressUtils;
@@ -455,7 +451,6 @@ static final class HttpClientHandler extends SocketAddress
455451
final BiFunction<? super HttpClientRequest, ? super NettyOutbound, ? extends Publisher<Void>>
456452
handler;
457453
final boolean compress;
458-
final UriEndpointFactory uriEndpointFactory;
459454
final WebsocketClientSpec websocketClientSpec;
460455
final BiPredicate<HttpClientRequest, HttpClientResponse>
461456
followRedirectPredicate;
@@ -468,7 +463,6 @@ static final class HttpClientHandler extends SocketAddress
468463
final Duration responseTimeout;
469464

470465
volatile UriEndpoint toURI;
471-
volatile String resourceUrl;
472466
volatile UriEndpoint fromURI;
473467
volatile Supplier<String>[] redirectedFrom;
474468
volatile boolean shouldRetry;
@@ -484,34 +478,10 @@ static final class HttpClientHandler extends SocketAddress
484478
this.proxyProvider = configuration.proxyProvider();
485479
this.responseTimeout = configuration.responseTimeout;
486480
this.defaultHeaders = configuration.headers;
487-
488-
String baseUrl = configuration.baseUrl;
489-
490-
this.uriEndpointFactory =
491-
new UriEndpointFactory(configuration.remoteAddress(), configuration.isSecure(), URI_ADDRESS_MAPPER);
492-
493481
this.websocketClientSpec = configuration.websocketClientSpec;
494482
this.shouldRetry = !configuration.retryDisabled;
495483
this.handler = configuration.body;
496-
497-
if (configuration.uri == null) {
498-
String uri = configuration.uriStr;
499-
500-
uri = uri == null ? "/" : uri;
501-
502-
if (baseUrl != null && uri.startsWith("/")) {
503-
if (baseUrl.endsWith("/")) {
504-
baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
505-
}
506-
uri = baseUrl + uri;
507-
}
508-
509-
this.toURI = uriEndpointFactory.createUriEndpoint(uri, configuration.websocketClientSpec != null);
510-
}
511-
else {
512-
this.toURI = uriEndpointFactory.createUriEndpoint(configuration.uri, configuration.websocketClientSpec != null);
513-
}
514-
this.resourceUrl = toURI.toExternalForm();
484+
this.toURI = UriEndpoint.create(configuration.uri, configuration.baseUrl, configuration.uriStr, configuration.remoteAddress(), configuration.isSecure(), configuration.websocketClientSpec != null);
515485
}
516486

517487
@Override
@@ -526,18 +496,16 @@ public SocketAddress get() {
526496

527497
Publisher<Void> requestWithBody(HttpClientOperations ch) {
528498
try {
529-
ch.resourceUrl = this.resourceUrl;
499+
UriEndpoint uriEndpoint = toURI;
500+
ch.uriEndpoint = uriEndpoint;
530501
ch.responseTimeout = responseTimeout;
531502

532-
UriEndpoint uri = toURI;
533503
HttpHeaders headers = ch.getNettyRequest()
534-
.setUri(uri.getPathAndQuery())
504+
.setUri(uriEndpoint.getRawUri())
535505
.setMethod(method)
536506
.setProtocolVersion(HttpVersion.HTTP_1_1)
537507
.headers();
538508

539-
ch.path = HttpOperations.resolvePath(ch.uri());
540-
541509
if (!defaultHeaders.isEmpty()) {
542510
headers.set(defaultHeaders);
543511
}
@@ -546,9 +514,8 @@ Publisher<Void> requestWithBody(HttpClientOperations ch) {
546514
headers.set(HttpHeaderNames.USER_AGENT, USER_AGENT);
547515
}
548516

549-
SocketAddress remoteAddress = uri.getRemoteAddress();
550517
if (!headers.contains(HttpHeaderNames.HOST)) {
551-
headers.set(HttpHeaderNames.HOST, resolveHostHeaderValue(remoteAddress));
518+
headers.set(HttpHeaderNames.HOST, uriEndpoint.getHostHeader());
552519
}
553520

554521
if (!headers.contains(HttpHeaderNames.ACCEPT)) {
@@ -608,46 +575,11 @@ Publisher<Void> requestWithBody(HttpClientOperations ch) {
608575
}
609576
}
610577

611-
static String resolveHostHeaderValue(@Nullable SocketAddress remoteAddress) {
612-
if (remoteAddress instanceof InetSocketAddress) {
613-
InetSocketAddress address = (InetSocketAddress) remoteAddress;
614-
String host = HttpUtil.formatHostnameForHttp(address);
615-
int port = address.getPort();
616-
if (port != 80 && port != 443) {
617-
host = host + ':' + port;
618-
}
619-
return host;
620-
}
621-
else {
622-
return "localhost";
623-
}
624-
}
625-
626578
void redirect(String to) {
627579
Supplier<String>[] redirectedFrom = this.redirectedFrom;
628-
UriEndpoint toURITemp;
629-
UriEndpoint from = toURI;
630-
SocketAddress address = from.getRemoteAddress();
631-
if (address instanceof InetSocketAddress) {
632-
try {
633-
URI redirectUri = new URI(to);
634-
if (!redirectUri.isAbsolute()) {
635-
URI requestUri = new URI(resourceUrl);
636-
redirectUri = requestUri.resolve(redirectUri);
637-
}
638-
toURITemp = uriEndpointFactory.createUriEndpoint(redirectUri, from.isWs());
639-
}
640-
catch (URISyntaxException e) {
641-
throw new IllegalArgumentException("Cannot resolve location header", e);
642-
}
643-
}
644-
else {
645-
toURITemp = uriEndpointFactory.createUriEndpoint(from, to, () -> address);
646-
}
647-
fromURI = from;
648-
toURI = toURITemp;
649-
resourceUrl = toURITemp.toExternalForm();
650-
this.redirectedFrom = addToRedirectedFromArray(redirectedFrom, from);
580+
fromURI = toURI;
581+
toURI = toURI.redirect(to);
582+
this.redirectedFrom = addToRedirectedFromArray(redirectedFrom, fromURI);
651583
}
652584

653585
@SuppressWarnings({"unchecked", "rawtypes"})

Diff for: reactor-netty-http/src/main/java/reactor/netty/http/client/HttpClientOperations.java

+4-6
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,7 @@ class HttpClientOperations extends HttpOperations<NettyInbound, NettyOutbound>
106106
final Sinks.One<HttpHeaders> trailerHeaders;
107107

108108
Supplier<String>[] redirectedFrom = EMPTY_REDIRECTIONS;
109-
String resourceUrl;
110-
String path;
109+
UriEndpoint uriEndpoint;
111110
Duration responseTimeout;
112111

113112
volatile ResponseState responseState;
@@ -140,8 +139,7 @@ class HttpClientOperations extends HttpOperations<NettyInbound, NettyOutbound>
140139
this.requestHeaders = replaced.requestHeaders;
141140
this.cookieEncoder = replaced.cookieEncoder;
142141
this.cookieDecoder = replaced.cookieDecoder;
143-
this.resourceUrl = replaced.resourceUrl;
144-
this.path = replaced.path;
142+
this.uriEndpoint = replaced.uriEndpoint;
145143
this.responseTimeout = replaced.responseTimeout;
146144
this.is100Continue = replaced.is100Continue;
147145
this.trailerHeaders = replaced.trailerHeaders;
@@ -504,12 +502,12 @@ public final String uri() {
504502

505503
@Override
506504
public final String fullPath() {
507-
return this.path;
505+
return uriEndpoint == null ? null : uriEndpoint.getPath();
508506
}
509507

510508
@Override
511509
public String resourceUrl() {
512-
return resourceUrl;
510+
return uriEndpoint == null ? null : uriEndpoint.toExternalForm();
513511
}
514512

515513
@Override

0 commit comments

Comments
 (0)