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);
}