private void doTunnelHandshake()

in iothub/device/iot-device-client/src/main/java/com/microsoft/azure/sdk/iot/device/transport/ProxiedSSLSocket.java [89:155]


    private void doTunnelHandshake(Socket tunnel, String host, int port) throws IOException
    {
        Charset byteEncoding = StandardCharsets.UTF_8;
        OutputStream out = tunnel.getOutputStream();
        String hostWithPort = host + ":" + port;

        String proxyConnectMessage = String.format("CONNECT %s %s\r\nHost: %s\r\nUser-Agent: %s\r\n", hostWithPort, HTTP_VERSION_1_1, hostWithPort, TransportUtils.USER_AGENT_STRING);
        if (this.proxyUsername != null && this.proxyPassword != null)
        {
            String base64EncodedCredentials = new String(Base64.encodeBase64(String.format("%s:%s", this.proxyUsername, new String(this.proxyPassword)).getBytes(byteEncoding)), byteEncoding);
            proxyConnectMessage += String.format("Proxy-Authorization: Basic %s\r\nUser-Agent: %s\r\n", base64EncodedCredentials, TransportUtils.USER_AGENT_STRING);
        }

        proxyConnectMessage += "\r\n";

        byte[] proxyConnectBytes = proxyConnectMessage.getBytes(byteEncoding);

        out.write(proxyConnectBytes);
        out.flush();

        //Cannot do any buffering while reading, only read what is relevant to the connect response
        HttpConnectResponseReader in = new HttpConnectResponseReader(tunnel.getInputStream(), byteEncoding);

        String connectResponse = in.readHttpConnectResponse();

        String[] connectResponseLines = connectResponse.split("\r\n");

        int connectResponseStart = 0;
        while (connectResponseLines[connectResponseStart].isEmpty())
        {
            connectResponseStart++;
        }

        //Expects the same http version in the response as the request
        String firstLine = connectResponseLines[connectResponseStart];
        if (!firstLine.startsWith(HTTP))
        {
            tunnel.close();
            throw new IOException(String.format("Unable to tunnel through %s:%d.  Expected first response line to start with %s, but proxy returns \"%s\"", host, port, HTTP, firstLine));
        }

        String[] replyStrParts = firstLine.split(" ");
        if (replyStrParts.length < 2)
        {
            tunnel.close();
            throw new IOException(String.format("Unable to tunnel through %s:%d. Expected proxy response to CONNECT to contain a space between http version and status code, but was %s", host, port, firstLine));
        }

        int connectResponseStatusCode;
        try
        {
            connectResponseStatusCode = Integer.parseInt(replyStrParts[1]);
        }
        catch (NumberFormatException e)
        {
            tunnel.close();
            throw new IOException(String.format("Unable to tunnel through %s:%d. Expected proxy response to CONNECT to contain a status code but status code could not be parsed. Response was %s", host, port, firstLine));
        }

        if (connectResponseStatusCode <= 199 || connectResponseStatusCode >= 300)
        {
            tunnel.close();
            throw new IOException(String.format("Unable to tunnel through %s:%d. Expected proxy response to CONNECT to return status code 2XX but status code was %d", host, port, connectResponseStatusCode));
        }

        log.trace("HTTP proxy responded to connect request with status {}, so the proxy connect was successful", connectResponseStatusCode);
    }