Skip to content

Commit 69d0ae2

Browse files
authored
Public API to get absolute URI of the request. (helidon-io#2441)
* Public API to get absolute URI of the request. * Absolute URI uses lazy value, added test. Signed-off-by: Tomas Langer <tomas.langer@oracle.com>
1 parent 7b1266f commit 69d0ae2

5 files changed

Lines changed: 69 additions & 44 deletions

File tree

webserver/jersey/src/main/java/io/helidon/webserver/jersey/JerseySupport.java

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,8 @@
1818

1919
import java.io.InputStream;
2020
import java.lang.reflect.Type;
21-
import java.net.MalformedURLException;
2221
import java.net.URI;
2322
import java.net.URISyntaxException;
24-
import java.net.URL;
2523
import java.security.Principal;
2624
import java.util.Collection;
2725
import java.util.Map;
@@ -154,30 +152,6 @@ private static synchronized ExecutorService getDefaultThreadPool(Config config)
154152
return DEFAULT_THREAD_POOL.get();
155153
}
156154

157-
private static URI requestUri(ServerRequest req) {
158-
try {
159-
// Use raw string representation and URL to avoid re-encoding chars like '%'
160-
URI partialUri = new URL(req.isSecure() ? "https" : "http", req.localAddress(),
161-
req.localPort(), req.path().absolute().toRawString()).toURI();
162-
StringBuilder sb = new StringBuilder(partialUri.toString());
163-
if (req.uri().toString().endsWith("/") && sb.charAt(sb.length() - 1) != '/') {
164-
sb.append('/');
165-
}
166-
167-
// unfortunately, the URI constructor encodes the 'query' and 'fragment' which is totally silly
168-
if (req.query() != null && !req.query().isEmpty()) {
169-
sb.append("?")
170-
.append(req.query());
171-
}
172-
if (req.fragment() != null && !req.fragment().isEmpty()) {
173-
sb.append("#")
174-
.append(req.fragment());
175-
}
176-
return new URI(sb.toString());
177-
} catch (URISyntaxException | MalformedURLException e) {
178-
throw new HttpException("Unable to parse request URL", Http.Status.BAD_REQUEST_400, e);
179-
}
180-
}
181155

182156
private static URI baseUri(ServerRequest req) {
183157
try {
@@ -255,7 +229,7 @@ private void doAccept(ServerRequest req, ServerResponse res) {
255229
CompletableFuture<Void> whenHandleFinishes = new CompletableFuture<>();
256230
ResponseWriter responseWriter = new ResponseWriter(res, req, whenHandleFinishes);
257231
ContainerRequest requestContext = new ContainerRequest(baseUri(req),
258-
requestUri(req),
232+
req.absoluteUri(),
259233
req.method().name(),
260234
new WebServerSecurityContext(),
261235
new WebServerPropertiesDelegate(req),

webserver/webserver/src/main/java/io/helidon/webserver/RequestRouting.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package io.helidon.webserver;
1818

19+
import java.net.URI;
1920
import java.util.ArrayList;
2021
import java.util.LinkedList;
2122
import java.util.List;
@@ -30,6 +31,7 @@
3031
import java.util.logging.Level;
3132
import java.util.logging.Logger;
3233

34+
import io.helidon.common.LazyValue;
3335
import io.helidon.common.context.Contexts;
3436
import io.helidon.common.http.AlreadyCompletedException;
3537
import io.helidon.common.http.Http;
@@ -233,6 +235,7 @@ private static class RoutedRequest extends Request {
233235
private final RoutedResponse response;
234236

235237
private final AtomicBoolean nexted = new AtomicBoolean(false);
238+
private final LazyValue<URI> lazyAbsoluteUri = LazyValue.create(super::absoluteUri);
236239

237240
/**
238241
* Creates new instance.
@@ -427,6 +430,11 @@ public Tracer tracer() {
427430
return WebTracingConfig.tracer(webServer());
428431
}
429432

433+
@Override
434+
public URI absoluteUri() {
435+
return lazyAbsoluteUri.get();
436+
}
437+
430438
private class ErrorRoutedRequest extends RoutedRequest {
431439
private final Throwable t;
432440

webserver/webserver/src/main/java/io/helidon/webserver/ServerRequest.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,14 @@
1616

1717
package io.helidon.webserver;
1818

19+
import java.net.MalformedURLException;
20+
import java.net.URI;
21+
import java.net.URISyntaxException;
22+
import java.net.URL;
1923
import java.util.Optional;
2024

2125
import io.helidon.common.context.Context;
26+
import io.helidon.common.http.Http;
2227
import io.helidon.common.http.HttpRequest;
2328
import io.helidon.media.common.MessageBodyReadableContent;
2429

@@ -136,4 +141,35 @@ public interface ServerRequest extends HttpRequest {
136141
* @return the tracer associated, or {@link io.opentracing.util.GlobalTracer#get()}
137142
*/
138143
Tracer tracer();
144+
145+
/**
146+
* Absolute URI of the incoming request, including query parameters and fragment.
147+
* The host and port are obtained from the interface this server listens on ({@code host} header is not used).
148+
*
149+
* @return the URI of incoming request
150+
*/
151+
default URI absoluteUri() {
152+
try {
153+
// Use raw string representation and URL to avoid re-encoding chars like '%'
154+
URI partialUri = new URL(isSecure() ? "https" : "http", localAddress(),
155+
localPort(), path().absolute().toRawString()).toURI();
156+
StringBuilder sb = new StringBuilder(partialUri.toString());
157+
if (uri().toString().endsWith("/") && sb.charAt(sb.length() - 1) != '/') {
158+
sb.append('/');
159+
}
160+
161+
// unfortunately, the URI constructor encodes the 'query' and 'fragment' which is totally silly
162+
if (query() != null && !query().isEmpty()) {
163+
sb.append("?")
164+
.append(query());
165+
}
166+
if (fragment() != null && !fragment().isEmpty()) {
167+
sb.append("#")
168+
.append(fragment());
169+
}
170+
return new URI(sb.toString());
171+
} catch (URISyntaxException | MalformedURLException e) {
172+
throw new HttpException("Unable to parse request URL", Http.Status.BAD_REQUEST_400, e);
173+
}
174+
}
139175
}

webserver/webserver/src/test/java/io/helidon/webserver/NettyWebServerTest.java

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package io.helidon.webserver;
1818

1919
import java.net.InetAddress;
20-
import java.security.SecureRandom;
2120
import java.util.Collections;
2221
import java.util.concurrent.CompletableFuture;
2322
import java.util.concurrent.CompletionException;
@@ -70,9 +69,6 @@ static void main(String[] args) throws InterruptedException {
7069
.port(8080)
7170
.bindAddress(InetAddress.getLoopbackAddress())
7271
.routing(routing((breq, bres) -> {
73-
long id = new SecureRandom().nextLong();
74-
System.out.println("Received request .. ID: " + id);
75-
7672
SubmissionPublisher<DataChunk> responsePublisher = new SubmissionPublisher<>(ForkJoinPool.commonPool(), 1024);
7773
responsePublisher.subscribe(bres);
7874

@@ -95,11 +91,7 @@ static void main(String[] args) throws InterruptedException {
9591
LOGGER.log(Level.WARNING,
9692
"An error occurred during the flow consumption!",
9793
ex);
98-
}, () -> {
99-
System.out.println("Final execution");
100-
responsePublisher.close();
101-
}, (Subscription s) -> {
102-
System.out.println("Subscribe");
94+
}, responsePublisher::close, (Subscription s) -> {
10395
subscription.set(s);
10496
s.request(1);
10597
bres.writeStatusAndHeaders(Http.Status.CREATED_201,
@@ -155,7 +147,7 @@ public void testSinglePortsSuccessStart() throws Exception {
155147
}
156148

157149
@Test
158-
public void testMultiplePortsSuccessStart() throws Exception {
150+
public void testMultiplePortsSuccessStart() {
159151
WebServer webServer = WebServer.builder()
160152
.addSocket(SocketConfiguration.create("1"))
161153
.addSocket(SocketConfiguration.create("2"))
@@ -164,8 +156,7 @@ public void testMultiplePortsSuccessStart() throws Exception {
164156
.build();
165157

166158
webServer.start()
167-
.toCompletableFuture()
168-
.join();
159+
.await();
169160

170161
try {
171162
assertThat(webServer.port(), greaterThan(0));
@@ -216,13 +207,11 @@ public void testManyPortsButTwoTheSame() throws Exception {
216207
assertStartFailure(webServer);
217208
}
218209

219-
private void assertStartFailure(WebServer webServer)
220-
throws InterruptedException {
210+
private void assertStartFailure(WebServer webServer) {
221211

222212
try {
223213
webServer.start()
224-
.toCompletableFuture()
225-
.join();
214+
.await();
226215

227216
fail("Should have failed!");
228217
} catch (CompletionException e) {

webserver/webserver/src/test/java/io/helidon/webserver/PlainTest.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2020 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2017, 2020 Oracle and/or its affiliates.
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.
@@ -32,6 +32,7 @@
3232
import io.helidon.common.http.DataChunk;
3333
import io.helidon.common.http.Http;
3434
import io.helidon.common.reactive.Multi;
35+
import io.helidon.webclient.WebClient;
3536
import io.helidon.webserver.utils.SocketHttpClient;
3637

3738
import org.hamcrest.collection.IsIterableWithSize;
@@ -41,6 +42,7 @@
4142
import org.junit.jupiter.api.Test;
4243

4344
import static org.hamcrest.CoreMatchers.containsString;
45+
import static org.hamcrest.CoreMatchers.endsWith;
4446
import static org.hamcrest.CoreMatchers.is;
4547
import static org.hamcrest.CoreMatchers.not;
4648
import static org.hamcrest.CoreMatchers.notNullValue;
@@ -119,6 +121,9 @@ private static void startServer(int port) {
119121
.get("/multi", (req, res) -> {
120122
res.send(Multi.just("test1","test2").map(i -> DataChunk.create(String.valueOf(i).getBytes())));
121123
})
124+
.get("/absoluteUri", (req, res) -> {
125+
res.send(req.absoluteUri().toString());
126+
})
122127
.any(Handler.create(String.class, (req, res, entity) -> {
123128
res.send("It works! Payload: " + entity);
124129
}))
@@ -432,6 +437,19 @@ public void testBadContentType() throws Exception {
432437
}
433438

434439

440+
@Test
441+
void testAbsouteUri() throws Exception {
442+
String result = WebClient.create()
443+
.get()
444+
.uri("http://localhost:" + webServer.port() + "/absoluteUri?a=b")
445+
.request(String.class)
446+
.await(5, TimeUnit.SECONDS);
447+
448+
assertThat(result, containsString("http://"));
449+
assertThat(result, containsString(String.valueOf(webServer.port())));
450+
assertThat(result, endsWith("/absoluteUri?a=b"));
451+
}
452+
435453
@Test
436454
public void testMulti() throws Exception {
437455
String s = SocketHttpClient.sendAndReceive("/multi",

0 commit comments

Comments
 (0)