Apache Http Components - How to timeout CONNECT request to a proxy? - proxy

Timeout Without Using Proxy
I start netcat in my local as follows, which basically listens to connections on port 9090:
netcat -l -p 9090
And using Apache HttpComponents, I create a connection to it with a timeout of 4 seconds..
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(4000)
.setConnectTimeout(4000)
.setConnectionRequestTimeout(4000)
.build();
HttpGet httpget = new HttpGet("http://127.0.0.1:9090");
httpget.setConfig(requestConfig);
try (CloseableHttpResponse response = HttpClients.createDefault().execute(httpget)) {}
In terminal (where I have netcat running) I see:
??]?D???;#???9?Mۡ?NR?w?{)?V?$?(=?&?*kj?
?5??98?#?'<?%?)g#? ?/??32?,?+?0??.?2???/??-?1???D
<!-- 4 seconds later -->
read(net): Connection reset by peer
In client side what I see is:
Exception in thread "main" org.apache.http.conn.ConnectTimeoutException:
Connect to 127.0.0.1:9090 [/127.0.0.1] failed: Read timed out
This is all expected.
Timeout Using Proxy
I change the client code slightly and configure a proxy, following the docs here.
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(4000)
.setConnectTimeout(4000)
.setConnectionRequestTimeout(4000)
.build();
HttpHost proxy = new HttpHost("127.0.0.1", 9090);
DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy);
CloseableHttpClient httpclient = HttpClients.custom()
.setRoutePlanner(routePlanner)
.build();
HttpGet httpget = new HttpGet("https://127.0.0.1:9090");
httpget.setConfig(requestConfig);
try (CloseableHttpResponse response = httpclient.execute(httpget)) {}
And again start netcat, and this time on serverside
CONNECT 127.0.0.1:9090 HTTP/1.1
Host: 127.0.0.1:9090
User-Agent: Apache-HttpClient/4.4.1 (Java/1.8.0_212)
But timeout is not working for CONNECT. I just wait forever..
How can I configure the httpclient to timeout for 4 seconds just like in the first case I described?

RequestConfig only take effect once a connection to the target via the specific route has been fully established . They do not apply to the SSL handshake or any CONNECT requests that take place prior to the main message exchange.
Configure socket timeout at the ConnectionManager level to ensure connection level operations time out after a certain period of inactivity.

One possibility:
// This part is the same..
httpget.setConfig(requestConfig);
ExecutorService executorService = Executors.newSingleThreadExecutor();
Callable<CloseableHttpResponse> callable = () -> {
try (CloseableHttpResponse response = httpclient.execute(httpget)) {
return response;
}
};
Future<CloseableHttpResponse> future = executorService.submit(callable);
try {
future.get(4, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
httpget.abort();
executorService.shutdownNow();
}
But I am open to other suggestions..

Related

UnknownHostException when trying to connect using websocket

I have a use case where I need to send 2 requests to the server. The output of first request is used in second request so the calls have to be synchronous. I am using ktor (OkHttp)client websocket for this. I am failing at first attempt to even connect to the server with this error
Exception in thread "main" java.net.UnknownHostException: https: nodename nor servname provided, or not known
I suspect I haven't split my url properly and thats why its not able to connect to host.
Couple of qns
Is there any benefit to using websocket instead of using 2 separate Http requests?
Is there a way I can just pass URL to the websocket request?
Best and easiest way to get response and send another request?
I have been able to find very limited documentation on ktor client websocket.
const val HOST = "https://sample.com"
const val PATH1 = "/path/to/config?val1=<val1>&val2=<val2>"
const val PATH2 = "/path/to/config?val=<response_from_first_req>"
fun useSocket() {
val client = HttpClient() {
install(WebSockets)
}
runBlocking {
client.webSocket(method = HttpMethod.Get, host = HOST, path = PATH1) {
val othersMessage = incoming.receive() as? Frame.Text
println(othersMessage?.readText())
println("Testing")
}
}
client.close()
}
Thanks in advance.

Closing connection to client in Vertx without restarting the server

I am using Vertx for my backend.
This is a TCP server and the server is connected to several clients.
I am trying to disconnect the client when reaching a certain condition.
The code that I used is as follows.
vertx.createNetServer(new NetServerOptions().setIdleTimeout(601))
.connectHandler(socket -> {
Instant start = Instant.now();
writerId = socket.writeHandlerID();
log.info("[TCPServerVerticle] first Tcp Server Instance Id : {}", serverId);
socket.handler(input -> { // input을 받았을 때 실행
writerId = socket.writeHandlerID();
SocketAddress localAddr = socket.localAddress();
SocketAddress remoteAddr = socket.remoteAddress();
central.setWriterId(writerId);
byte[] bytes = input.getBytes();
String inputString = Utils.byteArrayToHex(bytes);
central.inputMessage(inputString, writerId, vertx, localAddr, remoteAddr, versionMap).onComplete(ok -> {
String result = ok.result();
if (result.equals("nak")) {
socket.close();
}
});
});
When I execute this code, when the condition for "nak" is met, the server seems to restart and not the client.
Would there be a way to close the connection to the client without restarting the server?
Thank you in advance

How to detect if RSocket connection is successfull?

I have the following program through which I can detect the connection failure i.e doBeforeRetry.
Can someone tell me how to detect the successful connection or reconnection. I want to integrate a Health Check program that monitors this connection, but I am unable to capture the event that informs the connections is successfull.
Thanks
requester = RSocketRequester.builder()
.rsocketConnector(connector -> {
connector.reconnect(Retry
.fixedDelay(Integer.MAX_VALUE,Duration.ofSeconds(1))
.doBeforeRetry(e-> System.out.println("doBeforeRetry===>"+e))
.doAfterRetry(e-> System.out.println("doAfterRetry===>"+e))
);
connector.payloadDecoder(PayloadDecoder.ZERO_COPY);
}
).dataMimeType(MediaType.APPLICATION_CBOR)
.rsocketStrategies(strategies)
.tcp("localhost", 7999);
I achieved the detection of successful connection or reconnection with the following approach.
Client Side (Connection initialization)
Mono<RSocketRequester> requester = Mono.just(RSocketRequester.builder()
.rsocketConnector(
// connector configuration goes here
)
.dataMimeType(MediaType.APPLICATION_CBOR)
.setupRoute("client-handshake")
.setupData("caller-name")
.tcp("localhost", 7999)));
One the server side
#ConnectMapping("client-handshake")
public void connect(RSocketRequester requester, #Payload String callerName) {
LOG.info("Client Connection Handshake: [{}]", callerName);
requester
.route("server-handshake")
.data("I am server")
.retrieveMono(Void.class)
.subscribe();
}
On the client side, when I receive the callback on the below method, I detect the connection is successfull.
#MessageMapping("server-handshake")
public Mono<ConsumerPreference> handshake(final String response){
LOG.info("Server Connection Handshake received : Server message [{}]", response.getCallerName());
connectionSuccess.set(true);
return Mono.empty();
}else{
throw new InitializationException("Invalid response message received from Server");
}
}
Additionally, created a application level heartbeat to ensure, the liveliness of the connection.
If you want to know if it's actually healthy, you should probably have a side task that is polling the health of the RSocket, by sending something like a custom ping protocol to your backend. You could time that and confirm that you have a healthy connection, record latencies and success/failures.

No server chosen by com.mongodb.async.client.ClientSessionHelpe from cluster description ClusterDescription

I am trying to connect to aws DocumentDB with async mongoClient.
I created a DocumentDB cluster in aws and success connect via ssh command line.
I went over here and created MongoClient and success connected and insert events.
But when I tried create com.mongodb.async.client.MongoClient, connection failed with folowing error:
No server chosen by WritableServerSelector from cluster description
ClusterDescription{type=REPLICA_SET, connectionMode=MULTIPLE,
serverDescriptions=[ServerDescription{address=aws-cluster:27017,
type=UNKNOWN, state=CONNECTING,
exception={com.mongodb.MongoSocketReadTimeoutException: Timeout while
receiving message}, caused by
{io.netty.handler.timeout.ReadTimeoutException}}]}. Waiting for 30000
ms before timing out.
ClusterSettings clusterSettings = ClusterSettings.builder()
.applyConnectionString(new ConnectionString(connectionString)).build();
List<MongoCredential> credentials = new ArrayList<>();
credentials.add(
MongoCredential.createCredential(
mongoUserName,
mongoDBName,
mongoPassword));
MongoClientSettings settings = MongoClientSettings.builder()
.credentialList(credentials)
.clusterSettings(clusterSettings)
.streamFactoryFactory(new NettyStreamFactoryFactory())
.writeConcern(WriteConcern.ACKNOWLEDGED)
.build();
com.mongodb.async.client.MongoClient mongoClient = MongoClients.create(settings);
MongoDatabase testDB = mongoClient.getDatabase("myDB");
MongoCollection<Document> collection = testDB.getCollection("test");
Document doc = new Document("name", "MongoDB").append("type", "database");
//**trying insert document => here I got an error**
collection.insertOne(doc, new SingleResultCallback<Void>() {
#Override
public void onResult(final Void result, final Throwable t) {
System.out.println("Inserted!");
}
});
Do you have any ideas, why does it happen?
I solved it by using uri:
String uri = "mongodb://<username>:<Password>#<hostname>:27017/?ssl=true&ssl_ca_certs=cert";
MongoClientSettings settings = MongoClientSettings.builder()
.streamFactoryFactory(new NettyStreamFactoryFactory())
.applyConnectionString(new ConnectionString(uri))
.build();
com.mongodb.async.client.MongoClient mongoClient = MongoClients.create(settings);
I encountered a similar error , for me it was related to the TLS configs.
I disabled the TLS in documentDB https://docs.aws.amazon.com/documentdb/latest/developerguide/security.encryption.ssl.html
In my case I had to restart the cluster after disabling the TLS. (TLS was not needed for the use case). After the restart the connection was established successfully.

Redirect HTTP requests to HTTPS in netty

I am modifying elasticsearch code to configure HTTPS without x-pack and reverse proxies.
I modified initchannel() method in the netty4HttpServerTransport file , https is working fine,but i want to redirect http to https..
The code is,
char[] password = "your5663".toCharArray();
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream("C:/OpenSSL-Win64/bin/keystore1.jks"),password);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, password);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
TrustManager[] tm = tmf.getTrustManagers();
SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
sslContext .init( kmf.getKeyManagers(), tm, null);
SSLEngine sslengine = sslContext .createSSLEngine();
sslengine.setUseClientMode(false);
String[] DEFAULT_PROTOCOLS = { "TLSv1", "TLSv1.1", "TLSv1.2","TLSv1.3" };
String[] DEFAULT_CIPHERS = {"TLS_RSA_WITH_AES_128_CBC_SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA"};
sslengine.setEnabledProtocols(DEFAULT_PROTOCOLS);
sslengine.setEnabledCipherSuites(DEFAULT_CIPHERS);
SslHandler sslHandler = new SslHandler(sslengine);
ch.pipeline().addLast("ssl", sslHandler);
ch.pipeline().addAfter("ssl","handshake",new StringEventHandler());
How do i make http to https redirect in this code.
Redirect works on the payload (http) level, not ssl transport level. You would need to listen on both protocol (http and https) and on the http channel you can respond with redirect status code. Long story short - there is no direct place on in your code you can do that.
Very commonly a proxy server is used for this task. I am not sure if you can do it in elasticsearch, you can try to configure a filter servlet to check the protocol respond with a redirect. This may be helpful https://github.com/elastic/elasticsearch-transport-wares
Another fact - if the redirect is for service clients (not browser based ui), the clients may/will consider a redirect response an an error response. Depending on your environment - maybe you can just expose the ssl endpoint (no redirects) and clients will have to comply
Netty has a built in handler for this, OptionalSslHandler.
You put it at the front of your pipeline and it detects if the message is encrypted or not. If it is, then the message will be sent onto the normal SSL pipeline, if not then you can specify somewhere else to send it, e.g. to a 301 redirect to https.
You could either use this Netty version or make your own handler that does something similar.
However, to use the Netty version you will need to refactor slightly to produce a Netty SslContext io.netty.handler.ssl.SslContext, instead of an SSLEngine.
Something like this:
char[] password = "your5663".toCharArray();
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream("C:/OpenSSL-Win64/bin/keystore1.jks"),password);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, password);
SslContext sslContext = SslContextBuilder.forServer(keyManagerFactory).build();
ch.pipeline().addLast("ssl", sslHandler);
// this is an imaginary handler you create that sends HTTP a 301 to HTTPS
// non-SSL can be detected easily because there is no SslHandler on this channel
ch.pipeline().addLast("redirectHandler", new RedirectHandler());
ch.pipeline().addLast("handshake",new StringEventHandler());

Resources