Skip to content

Commit

Permalink
Addressing issue micronaut-projects#2283 where host is throwing NPE f…
Browse files Browse the repository at this point in the history
…or some names where authority is required
  • Loading branch information
coestre committed Oct 31, 2019
1 parent f9c8a69 commit ec2178b
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,23 @@
import io.micronaut.core.order.OrderUtil;
import io.micronaut.core.reflect.InstantiationUtils;
import io.micronaut.core.type.Argument;
import io.micronaut.core.util.*;
import io.micronaut.core.util.ArrayUtils;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.core.util.PathMatcher;
import io.micronaut.core.util.StringUtils;
import io.micronaut.core.util.Toggleable;
import io.micronaut.http.HttpResponseWrapper;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.MediaType;
import io.micronaut.http.MutableHttpHeaders;
import io.micronaut.http.MutableHttpRequest;
import io.micronaut.http.annotation.Filter;
import io.micronaut.http.bind.RequestBinderRegistry;
import io.micronaut.http.client.exceptions.*;
import io.micronaut.http.client.exceptions.ContentLengthExceededException;
import io.micronaut.http.client.exceptions.HttpClientErrorDecoder;
import io.micronaut.http.client.exceptions.HttpClientException;
import io.micronaut.http.client.exceptions.HttpClientResponseException;
import io.micronaut.http.client.exceptions.ReadTimeoutException;
import io.micronaut.http.client.filters.ClientServerContextFilter;
import io.micronaut.http.client.multipart.MultipartBody;
import io.micronaut.http.client.sse.RxSseClient;
Expand Down Expand Up @@ -118,8 +126,14 @@
import javax.inject.Named;
import java.io.Closeable;
import java.io.IOException;
import java.net.*;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.Proxy.Type;
import java.net.SocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
Expand Down Expand Up @@ -1739,9 +1753,25 @@ private ByteBuf charSequenceToByteBuf(CharSequence bodyValue, MediaType requestC
).asNativeBuffer();
}

private String getHostHeader(URI requestURI) {
StringBuilder host = new StringBuilder(requestURI.getHost());
int port = requestURI.getPort();
private String getHostHeader(URI uri) {
String hostOrAuthority = getHostOrAuthority(uri);
int port;
if (hostOrAuthority == null) {
throw new HttpClientException("URI specifies no host to connect to");
}
final int i = hostOrAuthority.indexOf(':');
if (i > -1) {
final String portStr = hostOrAuthority.substring(i + 1);
hostOrAuthority = hostOrAuthority.substring(0, i);
try {
port = Integer.parseInt(portStr);
} catch (NumberFormatException e) {
throw new HttpClientException("URI specifies an invalid port: " + portStr);
}
} else {
port = uri.getPort() > -1 ? uri.getPort() : DEFAULT_HTTP_PORT;
}
StringBuilder host = new StringBuilder(hostOrAuthority);
if (port > -1) {
if (port != 80 && port != 443) {
host.append(":").append(port);
Expand All @@ -1750,6 +1780,16 @@ private String getHostHeader(URI requestURI) {
return host.toString();
}

private String getHostOrAuthority(URI requestURI) {
String host = null;
if (requestURI != null && StringUtils.isNotEmpty(requestURI.getHost())) {
host = requestURI.getHost();
} else if (requestURI != null && StringUtils.isNotEmpty(requestURI.getAuthority())) {
host = requestURI.getAuthority();
}
return host;
}

private <I> void prepareHttpHeaders(URI requestURI, io.micronaut.http.HttpRequest<I> request, io.netty.handler.codec.http.HttpRequest nettyRequest, boolean permitsBody, boolean closeConnection) {
HttpHeaders headers = nettyRequest.headers();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
/*
* Copyright 2017-2019 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.micronaut.http.client

import io.micronaut.http.client.exceptions.HttpClientException
Expand Down Expand Up @@ -31,4 +46,17 @@ class ClientHostNameSpec extends Specification {
cleanup:
client.close()
}

void "test host name with dots and dashes and port"() {
when:
def client = HttpClient.create(new URL("https://slave1-6x8-build-agent-2.0.1-5h7sl:8080"))
client.toBlocking().retrieve("/")

then:
def e = thrown(HttpClientException)
e.message.contains('Connect Error: slave1-6x8-build-agent-2.0.1-5h7sl')

cleanup:
client.close()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright 2017-2019 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.micronaut.http.client

import io.micronaut.http.client.loadbalance.FixedLoadBalancer
import spock.lang.Specification
import spock.lang.Unroll

class DefaultHttpClientSpec extends Specification {

@Unroll
def "Host header for #url is #expected"() {
given:
def balancer = new FixedLoadBalancer(new URL(url))
def configuration = new DefaultHttpClientConfiguration()

when:
def header = new DefaultHttpClient(balancer, configuration).getHostHeader(new URI(url))

then:
header == expected

where:
url | expected
"https://slave1-6x8-build-agent-2.0.1-5h7sl:8080" | "slave1-6x8-build-agent-2.0.1-5h7sl:8080"
"https://foo_bar:8080" | "foo_bar:8080"
"https://foobar:8080" | "foobar:8080"
"http://foobar:8080" | "foobar:8080"
"http://foobar" | "foobar"
"http://foobar:80" | "foobar"
"https://foobar:443" | "foobar"
"https://service.url.com" | "service.url.com"
"https://service.url.com:91" | "service.url.com:91"
}

}
36 changes: 36 additions & 0 deletions http/src/test/groovy/io/micronaut/http/HttpRequestSpec.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2017-2019 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.micronaut.http

import spock.lang.Specification

class HttpRequestSpec extends Specification {

def "Converting to url and getting host and authority work as expected"() {
expect:
verifyAll {
assert new URI("https://foo_bar").toString() == "https://foo_bar"
assert HttpRequest.GET("https://localhost.company.com").getUri().toString() == "https://localhost.company.com"
assert HttpRequest.GET("https://localhost").getUri().toString() == "https://localhost"
assert HttpRequest.GET("https://foo_bar").getUri().toString() == "https://foo_bar"
assert HttpRequest.GET("https://slave1-6x8-build-agent-2.0.1-5h7sl").getUri().toString() == "https://slave1-6x8-build-agent-2.0.1-5h7sl"
assert HttpRequest.GET("https://slave1-6x8-build-agent-2.0.1-5h7sl:8080").getUri().toString() == "https://slave1-6x8-build-agent-2.0.1-5h7sl:8080"
assert HttpRequest.GET(new URI("https://slave1-6x8-build-agent-2.0.1-5h7sl:8080")).getUri().toString() == "https://slave1-6x8-build-agent-2.0.1-5h7sl:8080"
assert HttpRequest.GET(new URI("https://slave1-6x8-build-agent-2.0.1-5h7sl:8080")).getUri().getHost() == null
assert HttpRequest.GET(new URI("https://slave1-6x8-build-agent-2.0.1-5h7sl:8080")).getUri().getAuthority() == "slave1-6x8-build-agent-2.0.1-5h7sl"
}
}
}

0 comments on commit ec2178b

Please sign in to comment.