Kong / unirest-java

Unirest in Java: Simplified, lightweight HTTP client library.
http://kong.github.io/unirest-java/
MIT License
2.6k stars 592 forks source link

Duplicate requests #334

Closed francisco4challenge closed 4 years ago

francisco4challenge commented 4 years ago

Describe the bug Duplicate requests when dns resolves to 2 ips. I'm sending 1 request to my API, but it's receiving 2

To Reproduce Steps to reproduce the behavior:

  1. Just a simple post request will do val response = Unirest.post(url) .header("Content-Type", "application/json") .body("{ \"email\": \"$email\", \"password\": \"$password\" }")

Expected behavior Even with 2 resolved dns, i would expect to only receive 1

Environmental Data:

Additional context I'm running my aps in docker

ryber commented 4 years ago

The functionality for getting an IP for a request is done inside of Apache HTTP client org.apache.http.impl.conn.DefaultHttpClientConnectionOperator

It loops over each of the IPs and will attempt each only going to the next one when the previous one failed. I would suggest you enable debugging on that class to see what is going on.

(Apache 4.5.9 code I can't find on the internet for some dumb reason)

  final InetAddress[] addresses = host.getAddress() != null ?
                new InetAddress[] { host.getAddress() } : this.dnsResolver.resolve(host.getHostName());
        final int port = this.schemePortResolver.resolve(host);
        for (int i = 0; i < addresses.length; i++) {
            final InetAddress address = addresses[i];
            final boolean last = i == addresses.length - 1;

            Socket sock = sf.createSocket(context);
            sock.setSoTimeout(socketConfig.getSoTimeout());
            sock.setReuseAddress(socketConfig.isSoReuseAddress());
            sock.setTcpNoDelay(socketConfig.isTcpNoDelay());
            sock.setKeepAlive(socketConfig.isSoKeepAlive());
            if (socketConfig.getRcvBufSize() > 0) {
                sock.setReceiveBufferSize(socketConfig.getRcvBufSize());
            }
            if (socketConfig.getSndBufSize() > 0) {
                sock.setSendBufferSize(socketConfig.getSndBufSize());
            }

            final int linger = socketConfig.getSoLinger();
            if (linger >= 0) {
                sock.setSoLinger(true, linger);
            }
            conn.bind(sock);

            final InetSocketAddress remoteAddress = new InetSocketAddress(address, port);
            if (this.log.isDebugEnabled()) {
                this.log.debug("Connecting to " + remoteAddress);
            }
            try {
                sock = sf.connectSocket(
                        connectTimeout, sock, host, remoteAddress, localAddress, context);
                conn.bind(sock);
                if (this.log.isDebugEnabled()) {
                    this.log.debug("Connection established " + conn);
                }
                return;
            } catch (final SocketTimeoutException ex) {
                if (last) {
                    throw new ConnectTimeoutException(ex, host, addresses);
                }
            } catch (final ConnectException ex) {
                if (last) {
                    final String msg = ex.getMessage();
                    throw "Connection timed out".equals(msg)
                                    ? new ConnectTimeoutException(ex, host, addresses)
                                    : new HttpHostConnectException(ex, host, addresses);
                }
            } catch (final NoRouteToHostException ex) {
                if (last) {
                    throw ex;
                }
            }
            if (this.log.isDebugEnabled()) {
                this.log.debug("Connect to " + remoteAddress + " timed out. " +
                        "Connection will be retried using another IP address");
            }
        }
ryber commented 4 years ago

Is it possible to provide some guidance on how to actually test this? A simple docker maybe? In any case, Unirest itself doesn't perform this behavior and we would need to forward it on to the Apache team.