Underscores are valid in URIs https://www.ietf.org/rfc/rfc3986.txt but has long been ignored in jdk https://bugs.openjdk.java.net/browse/JDK-8019345
Could we provide a Spring URI class (and supporting classes like UriComponentsBuilder
) that supports this?
URI uri = UriComponentsBuilder.fromUriString("https://endpoint_with_underscore").build().toUri();
results in
java.lang.IllegalStateException: Could not create URI object: Illegal character in hostname at index 16: https://endpoint_with_underscore
at org.springframework.web.util.HierarchicalUriComponents.toUri(HierarchicalUriComponents.java:516)
at org.springframework.cloud.gateway.filter.ForwardRoutingFilterTests.uriWithUnderscore(ForwardRoutingFilterTests.java:75)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.mockito.internal.runners.DefaultInternalRunner$1$1.evaluate(DefaultInternalRunner.java:44)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:74)
at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:80)
at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39)
at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:163)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
Caused by: java.net.URISyntaxException: Illegal character in hostname at index 16: https://endpoint_with_underscore
at java.net.URI$Parser.fail(URI.java:2848)
at java.net.URI$Parser.parseHostname(URI.java:3387)
at java.net.URI$Parser.parseServer(URI.java:3236)
at java.net.URI$Parser.parseAuthority(URI.java:3155)
at java.net.URI$Parser.parseHierarchical(URI.java:3097)
at java.net.URI$Parser.parse(URI.java:3053)
at java.net.URI.<init>(URI.java:673)
at org.springframework.web.util.HierarchicalUriComponents.toUri(HierarchicalUriComponents.java:512)
... 29 more
Various issues: - https://github.com/r2dbc/r2dbc-spi/issues/155 - https://github.com/spring-cloud/spring-cloud-gateway/issues/851 - https://github.com/spring-cloud/spring-cloud-gateway/issues/543 - https://github.com/spring-cloud/spring-cloud-netflix/issues/263 - https://github.com/spring-cloud/spring-cloud-commons/issues/159
Comment From: rstoyanchev
@spencergibb isn't that what UriComponents
is?
/**
* Represents an immutable collection of URI components, mapping component type to
* String values. Contains convenience getters for all components. Effectively similar
* to java.net.URI, but with more powerful encoding options and support for
* URI template variables.
*/
The UriComponents#toUri()
is primarily for when you need to pass java.net.URI
somewhere but if you just need a parsed representation, you already have one.
Comment From: spencergibb
@rstoyanchev I hadn't even considered it. Looking at how gateway uses a uri, whenever it needs to forward it converts to a string and where it combines url's (ie from request and the information from service discovery) it is using UriComponentsBuilder
. I think for gateway this would work fine.
Where it won't work is for RestTemplate
and WebClient
via ClientHttpRequestInterceptor
and ExchangeFilterFunction
respectively since the request builders all take URI
.
Comment From: rstoyanchev
I think for gateway this would work fine.
Great!
Where it won't work is for RestTemplate and WebClient
This would be harder because it's built into contracts like ClientHttpConnector
and underneath it also depends on the underlying HTTP client. Reactor Netty client seems to avoid java.net.URI
for the most part except for WebSocket. Jetty HttpClient does accept String but turns it into URI.create(String)
immediately.
Comment From: rstoyanchev
In summary, we already provide UriComponents
as an alternative to java.net.URI
but we can't completely replace its use in lower level libraries we depend on. This can only be addressed in the JDK.