Skip to content

Commit 30c1bc8

Browse files
Transparent proxy: TP Loader Cloud SDK native integration
1 parent 48616ac commit 30c1bc8

File tree

2 files changed

+603
-0
lines changed

2 files changed

+603
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
package com.sap.cloud.sdk.cloudplatform.connectivity;
2+
3+
import java.io.IOException;
4+
import java.net.InetAddress;
5+
import java.net.InetSocketAddress;
6+
import java.net.Socket;
7+
import java.net.URI;
8+
import java.net.URISyntaxException;
9+
import java.net.UnknownHostException;
10+
11+
import javax.annotation.Nonnull;
12+
13+
import com.sap.cloud.sdk.cloudplatform.connectivity.exception.DestinationAccessException;
14+
15+
import io.vavr.control.Try;
16+
17+
public class TransparentProxyLoader implements DestinationLoader
18+
{
19+
public static final String HTTP_SCHEME = "http://";
20+
private static final String SCHEME_SEPARATOR = "://";
21+
private static final String PATH_SEPARATOR = "/";
22+
private static final String PORT_SEPARATOR = ":";
23+
24+
static String uri;
25+
static boolean isRegistered = false;
26+
static NetworkValidator networkValidator = new DefaultNetworkValidator();
27+
28+
public static void register( @Nonnull final String host )
29+
{
30+
registerLoader(host, null);
31+
}
32+
33+
public static void register( @Nonnull final String host, @Nonnull final Integer port )
34+
{
35+
registerLoader(host, port);
36+
}
37+
38+
private static void registerLoader( @Nonnull final String host, final Integer port )
39+
{
40+
if( isRegistered ) {
41+
throw new DestinationAccessException(
42+
"TransparentProxyLoader is already registered. Only one registration is allowed.");
43+
}
44+
45+
validateHostHasNoPath(host);
46+
47+
final String normalizedHost = normalizeHostWithScheme(host);
48+
final String hostForValidation = extractHostForValidation(normalizedHost);
49+
50+
final String finalUri;
51+
if( port != null ) {
52+
finalUri = normalizedHost + PORT_SEPARATOR + port;
53+
validateHostAndPortReachability(hostForValidation, port);
54+
} else {
55+
finalUri = normalizedHost;
56+
validateHostReachability(hostForValidation);
57+
}
58+
59+
TransparentProxyLoader.uri = finalUri;
60+
TransparentProxyLoader.isRegistered = true;
61+
DestinationAccessor.prependDestinationLoader(new TransparentProxyLoader());
62+
}
63+
64+
@Nonnull
65+
private static String normalizeHostWithScheme( @Nonnull final String host )
66+
{
67+
if( host.contains(SCHEME_SEPARATOR) ) {
68+
return host;
69+
}
70+
return HTTP_SCHEME + host;
71+
}
72+
73+
private static String extractHostForValidation( @Nonnull final String normalizedHost )
74+
{
75+
try {
76+
final URI uri = new URI(normalizedHost);
77+
return uri.getHost();
78+
}
79+
catch( final URISyntaxException e ) {
80+
String host = normalizedHost;
81+
if( host.contains(SCHEME_SEPARATOR) ) {
82+
host = host.substring(host.indexOf(SCHEME_SEPARATOR) + 3);
83+
}
84+
if( host.contains(PORT_SEPARATOR) ) {
85+
host = host.substring(0, host.indexOf(PORT_SEPARATOR));
86+
}
87+
if( host.contains(PATH_SEPARATOR) ) {
88+
host = host.substring(0, host.indexOf(PATH_SEPARATOR));
89+
}
90+
return host;
91+
}
92+
}
93+
94+
private static void validateHostReachability( @Nonnull final String host )
95+
{
96+
networkValidator.validateHostReachability(host);
97+
}
98+
99+
private static void validateHostAndPortReachability( @Nonnull final String host, final int port )
100+
{
101+
networkValidator.validateHostAndPortReachability(host, port);
102+
}
103+
104+
private static void validateHostHasNoPath( @Nonnull final String host )
105+
{
106+
try {
107+
final URI uri = new URI(host.contains(SCHEME_SEPARATOR) ? host : HTTP_SCHEME + host);
108+
final String path = uri.getPath();
109+
if( path != null && !path.isEmpty() ) {
110+
throw new DestinationAccessException(
111+
"Host '" + host + "' contains a path '" + path + "'. Paths are not allowed in host registration.");
112+
}
113+
}
114+
catch( final URISyntaxException e ) {
115+
final String hostToCheck =
116+
host.contains(SCHEME_SEPARATOR) ? host.substring(host.indexOf(SCHEME_SEPARATOR) + 3) : host;
117+
if( hostToCheck.contains(PATH_SEPARATOR) ) {
118+
final String pathPart = hostToCheck.substring(hostToCheck.indexOf(PATH_SEPARATOR));
119+
if( !"/".equals(pathPart) ) {
120+
throw new DestinationAccessException(
121+
"Host '"
122+
+ host
123+
+ "' contains a path '"
124+
+ pathPart
125+
+ "'. Paths are not allowed in host registration.");
126+
}
127+
}
128+
}
129+
}
130+
131+
@Nonnull
132+
@Override
133+
public Try<Destination> tryGetDestination( @Nonnull String destinationName )
134+
{
135+
return Try.success(TransparentProxyDestination.gateway(destinationName, uri).build());
136+
}
137+
138+
@Nonnull
139+
@Override
140+
public Try<Destination> tryGetDestination( @Nonnull String destinationName, @Nonnull DestinationOptions options )
141+
{
142+
return Try.success(TransparentProxyDestination.gateway(destinationName, uri).build());
143+
}
144+
145+
interface NetworkValidator
146+
{
147+
void validateHostReachability( @Nonnull String host )
148+
throws DestinationAccessException;
149+
150+
void validateHostAndPortReachability( @Nonnull String host, int port )
151+
throws DestinationAccessException;
152+
}
153+
154+
/**
155+
* Default implementation of network validation
156+
*/
157+
static class DefaultNetworkValidator implements NetworkValidator
158+
{
159+
private static final int HOST_REACH_TIMEOUT = 5000;
160+
161+
@Override
162+
public void validateHostReachability( @Nonnull final String host )
163+
throws DestinationAccessException
164+
{
165+
try {
166+
final InetAddress address = InetAddress.getByName(host);
167+
if( !address.isReachable(HOST_REACH_TIMEOUT) ) {
168+
throw new DestinationAccessException("Host '" + host + "' is not reachable");
169+
}
170+
}
171+
catch( final UnknownHostException e ) {
172+
throw new DestinationAccessException("Host '" + host + "' could not be resolved", e);
173+
}
174+
catch( final IOException e ) {
175+
throw new DestinationAccessException("Failed to check reachability of host '" + host + "'", e);
176+
}
177+
}
178+
179+
@Override
180+
public void validateHostAndPortReachability( @Nonnull final String host, final int port )
181+
throws DestinationAccessException
182+
{
183+
try( Socket socket = new Socket() ) {
184+
socket.connect(new InetSocketAddress(host, port), HOST_REACH_TIMEOUT);
185+
}
186+
catch( final UnknownHostException e ) {
187+
throw new DestinationAccessException("Host '" + host + "' could not be resolved", e);
188+
}
189+
catch( final IOException e ) {
190+
throw new DestinationAccessException("Host '" + host + "' on port " + port + " is not reachable", e);
191+
}
192+
}
193+
}
194+
195+
}

0 commit comments

Comments
 (0)