1
This commit is contained in:
95
mqtt/org/eclipse/paho/mqttv5/client/websocket/Base64.java
Normal file
95
mqtt/org/eclipse/paho/mqttv5/client/websocket/Base64.java
Normal file
@@ -0,0 +1,95 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* James Sutton - Bug 459142 - WebSocket support for the Java client.
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.client.websocket;
|
||||
|
||||
import java.util.prefs.AbstractPreferences;
|
||||
import java.util.prefs.BackingStoreException;
|
||||
|
||||
public class Base64 {
|
||||
|
||||
private static final Base64 instance = new Base64();
|
||||
private static final Base64Encoder encoder = instance.new Base64Encoder();
|
||||
|
||||
public static String encode (String s){
|
||||
encoder.putByteArray("akey", s.getBytes());
|
||||
return encoder.getBase64String();
|
||||
}
|
||||
|
||||
public static String encodeBytes (byte[] b){
|
||||
encoder.putByteArray("aKey", b);
|
||||
return encoder.getBase64String();
|
||||
|
||||
}
|
||||
|
||||
public class Base64Encoder extends AbstractPreferences {
|
||||
|
||||
private String base64String = null;
|
||||
|
||||
public Base64Encoder() {
|
||||
super(null, "");
|
||||
}
|
||||
|
||||
|
||||
protected void putSpi(String key, String value) {
|
||||
base64String = value;
|
||||
}
|
||||
|
||||
public String getBase64String() {
|
||||
return base64String;
|
||||
}
|
||||
|
||||
|
||||
protected String getSpi(String key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
protected void removeSpi(String key) {
|
||||
}
|
||||
|
||||
|
||||
protected void removeNodeSpi() throws BackingStoreException {
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected String[] keysSpi() throws BackingStoreException {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
protected String[] childrenNamesSpi() throws BackingStoreException {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
protected AbstractPreferences childSpi(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
protected void syncSpi() throws BackingStoreException {
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected void flushSpi() throws BackingStoreException {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
package org.eclipse.paho.mqttv5.client.websocket;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
class ExtendedByteArrayOutputStream extends ByteArrayOutputStream {
|
||||
|
||||
final WebSocketNetworkModule webSocketNetworkModule;
|
||||
final WebSocketSecureNetworkModule webSocketSecureNetworkModule;
|
||||
|
||||
ExtendedByteArrayOutputStream(WebSocketNetworkModule module) {
|
||||
this.webSocketNetworkModule = module;
|
||||
this.webSocketSecureNetworkModule = null;
|
||||
}
|
||||
|
||||
ExtendedByteArrayOutputStream(WebSocketSecureNetworkModule module) {
|
||||
this.webSocketNetworkModule = null;
|
||||
this.webSocketSecureNetworkModule = module;
|
||||
}
|
||||
|
||||
public void flush() throws IOException {
|
||||
final ByteBuffer byteBuffer;
|
||||
synchronized (this) {
|
||||
byteBuffer = ByteBuffer.wrap(toByteArray());
|
||||
reset();
|
||||
}
|
||||
WebSocketFrame frame = new WebSocketFrame((byte)0x02, true, byteBuffer.array());
|
||||
byte[] rawFrame = frame.encodeFrame();
|
||||
getSocketOutputStream().write(rawFrame);
|
||||
getSocketOutputStream().flush();
|
||||
|
||||
}
|
||||
|
||||
OutputStream getSocketOutputStream() throws IOException {
|
||||
|
||||
if(webSocketNetworkModule != null ){
|
||||
return webSocketNetworkModule.getSocketOutputStream();
|
||||
}
|
||||
if(webSocketSecureNetworkModule != null){
|
||||
return webSocketSecureNetworkModule.getSocketOutputStream();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* James Sutton - Bug 459142 - WebSocket support for the Java client.
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.client.websocket;
|
||||
|
||||
public class HandshakeFailedException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
}
|
@@ -0,0 +1,306 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* James Sutton - Bug 459142 - WebSocket support for the Java client.
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.client.websocket;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
public class WebSocketFrame {
|
||||
|
||||
public static final int frameLengthOverhead = 6;
|
||||
|
||||
private byte opcode;
|
||||
private boolean fin;
|
||||
private byte[] payload;
|
||||
private boolean closeFlag = false;
|
||||
|
||||
public byte getOpcode() {
|
||||
return opcode;
|
||||
}
|
||||
|
||||
public boolean isFin() {
|
||||
return fin;
|
||||
}
|
||||
|
||||
public byte[] getPayload() {
|
||||
return payload;
|
||||
}
|
||||
|
||||
public boolean isCloseFlag() {
|
||||
return closeFlag;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialise a new WebSocketFrame
|
||||
* @param opcode WebSocket Opcode
|
||||
* @param fin If it's final
|
||||
* @param payload The payload
|
||||
*/
|
||||
public WebSocketFrame(byte opcode, boolean fin, byte[] payload){
|
||||
this.opcode = opcode;
|
||||
this.fin = fin;
|
||||
this.payload = payload.clone();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialise WebSocketFrame from raw Data
|
||||
* @param rawFrame The raw byte buffer
|
||||
*/
|
||||
public WebSocketFrame (byte[] rawFrame){
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.wrap(rawFrame);
|
||||
|
||||
// First Byte: Fin, Reserved, Opcode
|
||||
byte b = buffer.get();
|
||||
setFinAndOpCode(b);
|
||||
|
||||
// Second Byte Masked & Initial Length
|
||||
b = buffer.get();
|
||||
boolean masked = ((b & 0x80) != 0);
|
||||
int payloadLength = (byte)(0x7F & b);
|
||||
int byteCount = 0;
|
||||
if(payloadLength == 0X7F){
|
||||
// 8 Byte Extended payload length
|
||||
byteCount = 8;
|
||||
} else if (payloadLength == 0X7E){
|
||||
// 2 bytes extended payload length
|
||||
byteCount = 2;
|
||||
}
|
||||
|
||||
// Decode the extended payload length
|
||||
while (--byteCount > 0){
|
||||
b = buffer.get();
|
||||
payloadLength |= (b & 0xFF) << (8 * byteCount);
|
||||
}
|
||||
|
||||
// Get the Masking key if masked
|
||||
byte[] maskingKey = null;
|
||||
if(masked) {
|
||||
maskingKey = new byte[4];
|
||||
buffer.get(maskingKey,0,4);
|
||||
}
|
||||
this.payload = new byte[payloadLength];
|
||||
buffer.get(this.payload,0,payloadLength);
|
||||
|
||||
// Demask payload if needed
|
||||
if(masked)
|
||||
{
|
||||
for(int i = 0; i < this.payload.length; i++){
|
||||
this.payload[i] ^= maskingKey[i % 4];
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the frames Fin flag and opcode.
|
||||
* @param incomingByte
|
||||
*/
|
||||
private void setFinAndOpCode(byte incomingByte){
|
||||
this.fin = ((incomingByte & 0x80) !=0);
|
||||
// Reserved bits, unused right now.
|
||||
// boolean rsv1 = ((incomingByte & 0x40) != 0);
|
||||
// boolean rsv2 = ((incomingByte & 0x20) != 0);
|
||||
// boolean rsv3 = ((incomingByte & 0x10) != 0);
|
||||
this.opcode = (byte)(incomingByte & 0x0F);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an input stream and parses it into a Websocket frame.
|
||||
* @param input The incoming {@link InputStream}
|
||||
* @throws IOException if an exception occurs whilst reading the input stream
|
||||
*/
|
||||
public WebSocketFrame(InputStream input) throws IOException {
|
||||
byte firstByte = (byte) input.read();
|
||||
setFinAndOpCode(firstByte);
|
||||
if(this.opcode == 2){
|
||||
byte maskLengthByte = (byte) input.read();
|
||||
boolean masked = ((maskLengthByte & 0x80) != 0);
|
||||
int payloadLength = (byte)(0x7F & maskLengthByte);
|
||||
int byteCount = 0;
|
||||
if(payloadLength == 0X7F){
|
||||
// 8 Byte Extended payload length
|
||||
byteCount = 8;
|
||||
} else if (payloadLength == 0X7E){
|
||||
// 2 bytes extended payload length
|
||||
byteCount = 2;
|
||||
}
|
||||
|
||||
// Decode the payload length
|
||||
if(byteCount > 0){
|
||||
payloadLength = 0;
|
||||
}
|
||||
while (--byteCount >= 0){
|
||||
maskLengthByte = (byte) input.read();
|
||||
payloadLength |= (maskLengthByte & 0xFF) << (8 * byteCount);
|
||||
}
|
||||
|
||||
// Get the masking key
|
||||
byte[] maskingKey = null;
|
||||
if(masked) {
|
||||
maskingKey = new byte[4];
|
||||
input.read(maskingKey,0,4);
|
||||
}
|
||||
|
||||
this.payload = new byte[payloadLength];
|
||||
int offsetIndex = 0;
|
||||
int tempLength = payloadLength;
|
||||
int bytesRead = 0;
|
||||
while (offsetIndex != payloadLength){
|
||||
bytesRead = input.read(this.payload,offsetIndex,tempLength);
|
||||
offsetIndex += bytesRead;
|
||||
tempLength -= bytesRead;
|
||||
}
|
||||
|
||||
|
||||
// Demask if needed
|
||||
if(masked)
|
||||
{
|
||||
for(int i = 0; i < this.payload.length; i++){
|
||||
this.payload[i] ^= maskingKey[i % 4];
|
||||
}
|
||||
}
|
||||
return;
|
||||
} else if(this.opcode == 8){
|
||||
// Closing connection with server
|
||||
closeFlag = true;
|
||||
} else {
|
||||
throw new IOException("Invalid Frame: Opcode: " +this.opcode);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Encodes the this WebSocketFrame into a byte array.
|
||||
* @return byte array
|
||||
*/
|
||||
public byte[] encodeFrame(){
|
||||
int length = this.payload.length + frameLengthOverhead;
|
||||
// Calculating overhead
|
||||
if(this.payload.length > 65535){
|
||||
length += 8;
|
||||
} else if(this.payload.length >= 126) {
|
||||
length += 2;
|
||||
}
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.allocate(length);
|
||||
appendFinAndOpCode(buffer, this.opcode, this.fin);
|
||||
byte[] mask = generateMaskingKey();
|
||||
appendLengthAndMask(buffer, this.payload.length, mask);
|
||||
|
||||
for(int i = 0; i < this.payload.length; i ++){
|
||||
buffer.put((byte)(this.payload[i] ^=mask[i % 4]));
|
||||
}
|
||||
|
||||
buffer.flip();
|
||||
return buffer.array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the Length and Mask to the buffer
|
||||
* @param buffer the outgoing {@link ByteBuffer}
|
||||
* @param length the length of the frame
|
||||
* @param mask The WebSocket Mask
|
||||
*/
|
||||
public static void appendLengthAndMask(ByteBuffer buffer, int length, byte[] mask){
|
||||
if(mask != null){
|
||||
appendLength(buffer, length, true);
|
||||
buffer.put(mask);
|
||||
} else {
|
||||
appendLength(buffer, length, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Appends the Length of the payload to the buffer
|
||||
* @param buffer
|
||||
* @param length
|
||||
* @param b
|
||||
*/
|
||||
private static void appendLength(ByteBuffer buffer, int length, boolean masked) {
|
||||
|
||||
if(length < 0){
|
||||
throw new IllegalArgumentException("Length cannot be negative");
|
||||
}
|
||||
|
||||
byte b = (masked?(byte)0x80:0x00);
|
||||
if(length > 0xFFFF){
|
||||
buffer.put((byte) (b | 0x7F));
|
||||
buffer.put((byte)0x00);
|
||||
buffer.put((byte)0x00);
|
||||
buffer.put((byte)0x00);
|
||||
buffer.put((byte)0x00);
|
||||
buffer.put((byte)((length >> 24) & 0xFF));
|
||||
buffer.put((byte)((length >> 16) & 0xFF));
|
||||
buffer.put((byte)((length >> 8) & 0xFF));
|
||||
buffer.put((byte)(length & 0xFF));
|
||||
} else if(length >= 0x7E){
|
||||
buffer.put((byte)(b | 0x7E));
|
||||
buffer.put((byte)(length >> 8));
|
||||
buffer.put((byte)(length & 0xFF));
|
||||
} else {
|
||||
buffer.put((byte)(b | length));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the Fin flag and the OpCode
|
||||
* @param buffer The outgoing buffer
|
||||
* @param opcode The Websocket OpCode
|
||||
* @param fin if this is a final frame
|
||||
*/
|
||||
public static void appendFinAndOpCode(ByteBuffer buffer, byte opcode, boolean fin){
|
||||
byte b = 0x00;
|
||||
// Add Fin flag
|
||||
if(fin){
|
||||
b |= 0x80;
|
||||
}
|
||||
//RSV 1,2,3 aren't important
|
||||
|
||||
// Add opcode
|
||||
b |= opcode & 0x0F;
|
||||
buffer.put(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random masking key
|
||||
* Nothing super secure, but enough
|
||||
* for websockets.
|
||||
* @return ByteArray containing the key;
|
||||
*/
|
||||
public static byte[] generateMaskingKey(){
|
||||
SecureRandom secureRandomGenerator = new SecureRandom();
|
||||
int a = secureRandomGenerator.nextInt(255);
|
||||
int b = secureRandomGenerator.nextInt(255);
|
||||
int c = secureRandomGenerator.nextInt(255);
|
||||
int d = secureRandomGenerator.nextInt(255);
|
||||
return new byte[] {(byte) a,(byte) b,(byte) c,(byte) d};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,230 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* James Sutton - Bug 459142 - WebSocket support for the Java client.
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.client.websocket;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Helper class to execute a WebSocket Handshake.
|
||||
*/
|
||||
public class WebSocketHandshake {
|
||||
|
||||
// Do not change: https://tools.ietf.org/html/rfc6455#section-1.3
|
||||
private static final String ACCEPT_SALT = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
private static final String SHA1_PROTOCOL = "SHA1";
|
||||
private static final String HTTP_HEADER_SEC_WEBSOCKET_ACCEPT = "sec-websocket-accept";
|
||||
private static final String HTTP_HEADER_UPGRADE = "upgrade";
|
||||
private static final String HTTP_HEADER_UPGRADE_WEBSOCKET = "websocket";
|
||||
private static final String EMPTY = "";
|
||||
private static final String LINE_SEPARATOR = "\r\n";
|
||||
private static final String HTTP_HEADER_CONNECTION = "connection";
|
||||
private static final String HTTP_HEADER_CONNECTION_VALUE = "upgrade";
|
||||
private static final String HTTP_HEADER_SEC_WEBSOCKET_PROTOCOL = "sec-websocket-protocol";
|
||||
|
||||
InputStream input;
|
||||
OutputStream output;
|
||||
String uri;
|
||||
String host;
|
||||
int port;
|
||||
Map<String, String> customWebSocketHeaders;
|
||||
|
||||
public WebSocketHandshake(InputStream input, OutputStream output, String uri, String host, int port, Map<String, String> customWebSocketHeaders) {
|
||||
this.input = input;
|
||||
this.output = output;
|
||||
this.uri = uri;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.customWebSocketHeaders = customWebSocketHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a Websocket Handshake. Will throw an IOException if the handshake
|
||||
* fails
|
||||
*
|
||||
* @throws IOException
|
||||
* thrown if an exception occurs during the handshake
|
||||
*/
|
||||
public void execute() throws IOException {
|
||||
byte[] key = new byte[16];
|
||||
System.arraycopy(UUID.randomUUID().toString().getBytes(), 0, key, 0, 16);
|
||||
String b64Key = Base64.encodeBytes(key);
|
||||
sendHandshakeRequest(b64Key);
|
||||
receiveHandshakeResponse(b64Key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and sends the HTTP Header GET Request for the socket.
|
||||
*
|
||||
* @param key
|
||||
* Base64 encoded key
|
||||
* @throws IOException
|
||||
*/
|
||||
private void sendHandshakeRequest(String key) {
|
||||
try {
|
||||
String path = "/mqtt";
|
||||
URI srvUri = new URI(uri);
|
||||
if (srvUri.getRawPath() != null && !srvUri.getRawPath().isEmpty()) {
|
||||
path = srvUri.getRawPath();
|
||||
if (srvUri.getRawQuery() != null && !srvUri.getRawQuery().isEmpty()) {
|
||||
path += "?" + srvUri.getRawQuery();
|
||||
}
|
||||
}
|
||||
|
||||
PrintWriter pw = new PrintWriter(output);
|
||||
pw.print("GET " + path + " HTTP/1.1" + LINE_SEPARATOR);
|
||||
if (port != 80 && port != 443) {
|
||||
pw.print("Host: " + host + ":" + port + LINE_SEPARATOR);
|
||||
} else {
|
||||
pw.print("Host: " + host + LINE_SEPARATOR);
|
||||
}
|
||||
|
||||
pw.print("Upgrade: websocket" + LINE_SEPARATOR);
|
||||
pw.print("Connection: Upgrade" + LINE_SEPARATOR);
|
||||
pw.print("Sec-WebSocket-Key: " + key + LINE_SEPARATOR);
|
||||
pw.print("Sec-WebSocket-Protocol: mqtt" + LINE_SEPARATOR);
|
||||
pw.print("Sec-WebSocket-Version: 13" + LINE_SEPARATOR);
|
||||
|
||||
if (customWebSocketHeaders != null) {
|
||||
customWebSocketHeaders.entrySet().forEach(entry ->
|
||||
pw.print(entry.getKey() + ": " + entry.getValue() + LINE_SEPARATOR)
|
||||
);
|
||||
}
|
||||
|
||||
String userInfo = srvUri.getUserInfo();
|
||||
if (userInfo != null) {
|
||||
pw.print("Authorization: Basic " + Base64.encode(userInfo) + LINE_SEPARATOR);
|
||||
}
|
||||
|
||||
pw.print(LINE_SEPARATOR);
|
||||
pw.flush();
|
||||
} catch (URISyntaxException e) {
|
||||
throw new IllegalStateException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives the Handshake response and verifies that it is valid.
|
||||
*
|
||||
* @param key
|
||||
* Base64 encoded key
|
||||
* @throws IOException
|
||||
*/
|
||||
private void receiveHandshakeResponse(String key) throws IOException {
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(input));
|
||||
ArrayList<String> responseLines = new ArrayList<>();
|
||||
String line = in.readLine();
|
||||
if (line == null) {
|
||||
throw new IOException(
|
||||
"WebSocket Response header: Invalid response from Server, It may not support WebSockets.");
|
||||
}
|
||||
while (!line.equals(EMPTY)) {
|
||||
responseLines.add(line);
|
||||
line = in.readLine();
|
||||
}
|
||||
Map<String, String> headerMap = getHeaders(responseLines);
|
||||
|
||||
String connectionHeader = headerMap.get(HTTP_HEADER_CONNECTION);
|
||||
if (connectionHeader == null || connectionHeader.equalsIgnoreCase(HTTP_HEADER_CONNECTION_VALUE)) {
|
||||
throw new IOException("WebSocket Response header: Incorrect connection header");
|
||||
}
|
||||
|
||||
String upgradeHeader = headerMap.get(HTTP_HEADER_UPGRADE);
|
||||
if (upgradeHeader == null || !upgradeHeader.toLowerCase().contains(HTTP_HEADER_UPGRADE_WEBSOCKET)) {
|
||||
throw new IOException("WebSocket Response header: Incorrect upgrade.");
|
||||
}
|
||||
|
||||
String secWebsocketProtocolHeader = headerMap.get(HTTP_HEADER_SEC_WEBSOCKET_PROTOCOL);
|
||||
if (secWebsocketProtocolHeader == null) {
|
||||
throw new IOException("WebSocket Response header: empty sec-websocket-protocol");
|
||||
}
|
||||
|
||||
if (!headerMap.containsKey(HTTP_HEADER_SEC_WEBSOCKET_ACCEPT)) {
|
||||
throw new IOException("WebSocket Response header: Missing Sec-WebSocket-Accept");
|
||||
}
|
||||
|
||||
try {
|
||||
verifyWebSocketKey(key, (String) headerMap.get(HTTP_HEADER_SEC_WEBSOCKET_ACCEPT));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IOException(e.getMessage());
|
||||
} catch (HandshakeFailedException e) {
|
||||
throw new IOException("WebSocket Response header: Incorrect Sec-WebSocket-Key");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Hashmap of HTTP headers
|
||||
*
|
||||
* @param headers
|
||||
* ArrayList<String> of headers
|
||||
* @return A Hashmap<String, String> of the headers
|
||||
*/
|
||||
private Map<String, String> getHeaders(ArrayList<String> headers) {
|
||||
Map<String, String> headerMap = new HashMap<>();
|
||||
for (int i = 1; i < headers.size(); i++) {
|
||||
String headerPre = headers.get(i);
|
||||
String[] header = headerPre.split(":");
|
||||
headerMap.put(header[0].toLowerCase(), header[1]);
|
||||
}
|
||||
return headerMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the Accept key provided is correctly built from the original
|
||||
* key sent.
|
||||
*
|
||||
* @param key
|
||||
* @param accept
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws HandshakeFailedException
|
||||
*/
|
||||
private void verifyWebSocketKey(String key, String accept)
|
||||
throws NoSuchAlgorithmException, HandshakeFailedException {
|
||||
// We build up the accept in the same way the server should
|
||||
// then we check that the response is the same.
|
||||
byte[] sha1Bytes = sha1(key + ACCEPT_SALT);
|
||||
String encodedSha1Bytes = Base64.encodeBytes(sha1Bytes).trim();
|
||||
if (!encodedSha1Bytes.equals(accept.trim())) {
|
||||
throw new HandshakeFailedException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sha1 byte array of the provided string.
|
||||
*
|
||||
* @param input
|
||||
* @return
|
||||
* @throws NoSuchAlgorithmException
|
||||
*/
|
||||
private byte[] sha1(String input) throws NoSuchAlgorithmException {
|
||||
MessageDigest mDigest = MessageDigest.getInstance(SHA1_PROTOCOL);
|
||||
return mDigest.digest(input.getBytes());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,111 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* James Sutton - Bug 459142 - WebSocket support for the Java client.
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.client.websocket;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PipedInputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import javax.net.SocketFactory;
|
||||
|
||||
import org.eclipse.paho.mqttv5.client.internal.TCPNetworkModule;
|
||||
import org.eclipse.paho.mqttv5.client.logging.Logger;
|
||||
import org.eclipse.paho.mqttv5.client.logging.LoggerFactory;
|
||||
import org.eclipse.paho.mqttv5.common.MqttException;
|
||||
|
||||
public class WebSocketNetworkModule extends TCPNetworkModule {
|
||||
|
||||
private static final String CLASS_NAME = WebSocketNetworkModule.class.getName();
|
||||
private Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT, CLASS_NAME);
|
||||
|
||||
private String uri;
|
||||
private String host;
|
||||
private int port;
|
||||
private PipedInputStream pipedInputStream;
|
||||
private WebSocketReceiver webSocketReceiver;
|
||||
ByteBuffer recievedPayload;
|
||||
Map<String, String> customWebSocketHeaders;
|
||||
|
||||
/**
|
||||
* Overrides the flush method.
|
||||
* This allows us to encode the MQTT payload into a WebSocket
|
||||
* Frame before passing it through to the real socket.
|
||||
*/
|
||||
private ByteArrayOutputStream outputStream = new ExtendedByteArrayOutputStream(this);
|
||||
|
||||
public WebSocketNetworkModule(SocketFactory factory, String uri, String host, int port, String resourceContext){
|
||||
super(factory, host, port, resourceContext);
|
||||
this.uri = uri;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.pipedInputStream = new PipedInputStream();
|
||||
|
||||
log.setResourceName(resourceContext);
|
||||
}
|
||||
|
||||
public void start() throws IOException, MqttException {
|
||||
super.start();
|
||||
WebSocketHandshake handshake = new WebSocketHandshake(getSocketInputStream(), getSocketOutputStream(), uri, host, port, customWebSocketHeaders);
|
||||
handshake.execute();
|
||||
this.webSocketReceiver = new WebSocketReceiver(getSocketInputStream(), pipedInputStream);
|
||||
webSocketReceiver.start("webSocketReceiver");
|
||||
}
|
||||
|
||||
OutputStream getSocketOutputStream() throws IOException {
|
||||
return super.getOutputStream();
|
||||
}
|
||||
|
||||
InputStream getSocketInputStream() throws IOException {
|
||||
return super.getInputStream();
|
||||
}
|
||||
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return pipedInputStream;
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() throws IOException {
|
||||
return outputStream;
|
||||
}
|
||||
|
||||
public void setCustomWebSocketHeaders(Map<String, String> customWebSocketHeaders) {
|
||||
this.customWebSocketHeaders = customWebSocketHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the module, by closing the TCP socket.
|
||||
*/
|
||||
public void stop() throws IOException {
|
||||
// Creating Close Frame
|
||||
WebSocketFrame frame = new WebSocketFrame((byte)0x08, true, "1000".getBytes());
|
||||
byte[] rawFrame = frame.encodeFrame();
|
||||
getSocketOutputStream().write(rawFrame);
|
||||
getSocketOutputStream().flush();
|
||||
|
||||
if(webSocketReceiver != null){
|
||||
webSocketReceiver.stop();
|
||||
}
|
||||
super.stop();
|
||||
}
|
||||
|
||||
public String getServerURI() {
|
||||
return "ws://" + host + ":" + port;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.client.websocket;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.net.SocketFactory;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import org.eclipse.paho.mqttv5.client.MqttClientException;
|
||||
import org.eclipse.paho.mqttv5.client.MqttConnectionOptions;
|
||||
import org.eclipse.paho.mqttv5.client.internal.NetworkModule;
|
||||
import org.eclipse.paho.mqttv5.client.spi.NetworkModuleFactory;
|
||||
import org.eclipse.paho.mqttv5.common.ExceptionHelper;
|
||||
import org.eclipse.paho.mqttv5.common.MqttException;
|
||||
|
||||
public class WebSocketNetworkModuleFactory implements NetworkModuleFactory {
|
||||
|
||||
@Override
|
||||
public Set<String> getSupportedUriSchemes() {
|
||||
return Collections.unmodifiableSet(new HashSet<>(Arrays.asList("ws")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateURI(URI brokerUri) throws IllegalArgumentException {
|
||||
// so specific requirements so far
|
||||
}
|
||||
|
||||
@Override
|
||||
public NetworkModule createNetworkModule(URI brokerUri, MqttConnectionOptions options, String clientId)
|
||||
throws MqttException
|
||||
{
|
||||
String host = brokerUri.getHost();
|
||||
int port = brokerUri.getPort(); // -1 if not defined
|
||||
if (port == -1) {
|
||||
port = 80;
|
||||
}
|
||||
SocketFactory factory = options.getSocketFactory();
|
||||
if (factory == null) {
|
||||
factory = SocketFactory.getDefault();
|
||||
} else if (factory instanceof SSLSocketFactory) {
|
||||
throw ExceptionHelper.createMqttException(MqttClientException.REASON_CODE_SOCKET_FACTORY_MISMATCH);
|
||||
}
|
||||
WebSocketNetworkModule netModule = new WebSocketNetworkModule(factory, brokerUri.toString(), host, port,
|
||||
clientId);
|
||||
netModule.setConnectTimeout(options.getConnectionTimeout());
|
||||
netModule.setCustomWebSocketHeaders(options.getCustomWebSocketHeaders());
|
||||
return netModule;
|
||||
}
|
||||
}
|
@@ -0,0 +1,149 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* James Sutton - Bug 459142 - WebSocket support for the Java client.
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.client.websocket;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
import java.net.SocketTimeoutException;
|
||||
|
||||
import org.eclipse.paho.mqttv5.client.logging.Logger;
|
||||
import org.eclipse.paho.mqttv5.client.logging.LoggerFactory;
|
||||
|
||||
public class WebSocketReceiver implements Runnable{
|
||||
|
||||
private static final String CLASS_NAME = WebSocketReceiver.class.getName();
|
||||
private Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT, CLASS_NAME);
|
||||
|
||||
private boolean running = false;
|
||||
private boolean stopping = false;
|
||||
private final Object lifecycle = new Object();
|
||||
private InputStream input;
|
||||
private Thread receiverThread = null;
|
||||
private volatile boolean receiving;
|
||||
private PipedOutputStream pipedOutputStream;
|
||||
|
||||
public WebSocketReceiver(InputStream input, PipedInputStream pipedInputStream) throws IOException{
|
||||
this.input = input;
|
||||
this.pipedOutputStream = new PipedOutputStream();
|
||||
pipedInputStream.connect(pipedOutputStream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts up the WebSocketReceiver's thread
|
||||
* @param threadName The name of the thread
|
||||
*/
|
||||
public void start(String threadName){
|
||||
final String methodName = "start";
|
||||
//@TRACE 855=starting
|
||||
log.fine(CLASS_NAME, methodName, "855");
|
||||
synchronized (lifecycle) {
|
||||
if(!running) {
|
||||
running = true;
|
||||
receiverThread = new Thread(this, threadName);
|
||||
receiverThread.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops this WebSocketReceiver's thread.
|
||||
* This call will block.
|
||||
*/
|
||||
public void stop() {
|
||||
final String methodName = "stop";
|
||||
stopping = true;
|
||||
boolean closed = false;
|
||||
synchronized (lifecycle) {
|
||||
//@TRACE 850=stopping
|
||||
log.fine(CLASS_NAME,methodName, "850");
|
||||
if(running) {
|
||||
running = false;
|
||||
receiving = false;
|
||||
closed = true;
|
||||
closeOutputStream();
|
||||
|
||||
}
|
||||
}
|
||||
if(closed && !Thread.currentThread().equals(receiverThread) && (receiverThread != null)) {
|
||||
try {
|
||||
// Wait for the thread to finish
|
||||
//This must not happen in the synchronized block, otherwise we can deadlock ourselves!
|
||||
receiverThread.join();
|
||||
} catch (InterruptedException ex) {
|
||||
// Interrupted Exception
|
||||
}
|
||||
}
|
||||
receiverThread = null;
|
||||
//@TRACE 851=stopped
|
||||
log.fine(CLASS_NAME, methodName, "851");
|
||||
}
|
||||
|
||||
public void run() {
|
||||
final String methodName = "run";
|
||||
|
||||
while (running && (input != null)) {
|
||||
try {
|
||||
//@TRACE 852=network read message
|
||||
log.fine(CLASS_NAME, methodName, "852");
|
||||
receiving = input.available() > 0;
|
||||
WebSocketFrame incomingFrame = new WebSocketFrame(input);
|
||||
if(!incomingFrame.isCloseFlag()){
|
||||
for(int i = 0; i < incomingFrame.getPayload().length; i++){
|
||||
pipedOutputStream.write(incomingFrame.getPayload()[i]);
|
||||
}
|
||||
|
||||
pipedOutputStream.flush();
|
||||
} else {
|
||||
if(!stopping){
|
||||
throw new IOException("Server sent a WebSocket Frame with the Stop OpCode");
|
||||
}
|
||||
}
|
||||
|
||||
receiving = false;
|
||||
|
||||
} catch (SocketTimeoutException ex) {
|
||||
// Ignore SocketTimeoutException
|
||||
} catch (IOException ex) {
|
||||
// Exception occurred whilst reading the stream.
|
||||
this.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void closeOutputStream(){
|
||||
try {
|
||||
pipedOutputStream.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the receiving state.
|
||||
*
|
||||
* @return true if the receiver is receiving data, false otherwise.
|
||||
*/
|
||||
public boolean isReceiving(){
|
||||
return receiving;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,111 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*
|
||||
* Contributors:
|
||||
* James Sutton - Bug 459142 - WebSocket support for the Java client.
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.client.websocket;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PipedInputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
import org.eclipse.paho.mqttv5.client.internal.SSLNetworkModule;
|
||||
import org.eclipse.paho.mqttv5.client.logging.Logger;
|
||||
import org.eclipse.paho.mqttv5.client.logging.LoggerFactory;
|
||||
import org.eclipse.paho.mqttv5.common.MqttException;
|
||||
|
||||
public class WebSocketSecureNetworkModule extends SSLNetworkModule{
|
||||
|
||||
private static final String CLASS_NAME = WebSocketSecureNetworkModule.class.getName();
|
||||
private Logger log = LoggerFactory.getLogger(LoggerFactory.MQTT_CLIENT_MSG_CAT, CLASS_NAME);
|
||||
|
||||
private PipedInputStream pipedInputStream;
|
||||
private WebSocketReceiver webSocketReceiver;
|
||||
private String uri;
|
||||
private String host;
|
||||
private int port;
|
||||
ByteBuffer recievedPayload;
|
||||
Map<String, String> customWebSocketHeaders;
|
||||
|
||||
/**
|
||||
* Overrides the flush method.
|
||||
* This allows us to encode the MQTT payload into a WebSocket
|
||||
* Frame before passing it through to the real socket.
|
||||
*/
|
||||
private ByteArrayOutputStream outputStream = new ExtendedByteArrayOutputStream(this);
|
||||
|
||||
public WebSocketSecureNetworkModule(SSLSocketFactory factory, String uri, String host, int port, String clientId) {
|
||||
super(factory, host, port, clientId);
|
||||
this.uri = uri;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.pipedInputStream = new PipedInputStream();
|
||||
log.setResourceName(clientId);
|
||||
}
|
||||
|
||||
public void start() throws IOException, MqttException {
|
||||
super.start();
|
||||
WebSocketHandshake handshake = new WebSocketHandshake(super.getInputStream(), super.getOutputStream(), uri, host, port, customWebSocketHeaders);
|
||||
handshake.execute();
|
||||
this.webSocketReceiver = new WebSocketReceiver(getSocketInputStream(), pipedInputStream);
|
||||
webSocketReceiver.start("WssSocketReceiver");
|
||||
|
||||
}
|
||||
|
||||
OutputStream getSocketOutputStream() throws IOException {
|
||||
return super.getOutputStream();
|
||||
}
|
||||
|
||||
InputStream getSocketInputStream() throws IOException {
|
||||
return super.getInputStream();
|
||||
}
|
||||
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return pipedInputStream;
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() throws IOException {
|
||||
return outputStream;
|
||||
}
|
||||
|
||||
public void setCustomWebSocketHeaders(Map<String, String> customWebSocketHeaders) {
|
||||
this.customWebSocketHeaders = customWebSocketHeaders;
|
||||
}
|
||||
|
||||
public void stop() throws IOException {
|
||||
// Creating Close Frame
|
||||
WebSocketFrame frame = new WebSocketFrame((byte)0x08, true, "1000".getBytes());
|
||||
byte[] rawFrame = frame.encodeFrame();
|
||||
getSocketOutputStream().write(rawFrame);
|
||||
getSocketOutputStream().flush();
|
||||
|
||||
if(webSocketReceiver != null){
|
||||
webSocketReceiver.stop();
|
||||
}
|
||||
super.stop();
|
||||
}
|
||||
|
||||
public String getServerURI() {
|
||||
return "wss://" + host + ":" + port;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0
|
||||
* and the Eclipse Distribution License is available at
|
||||
* https://www.eclipse.org/org/documents/edl-v10.php
|
||||
*/
|
||||
package org.eclipse.paho.mqttv5.client.websocket;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.net.SocketFactory;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import org.eclipse.paho.mqttv5.client.MqttClientException;
|
||||
import org.eclipse.paho.mqttv5.client.MqttConnectionOptions;
|
||||
import org.eclipse.paho.mqttv5.client.internal.NetworkModule;
|
||||
import org.eclipse.paho.mqttv5.client.internal.SSLNetworkModule;
|
||||
import org.eclipse.paho.mqttv5.client.security.SSLSocketFactoryFactory;
|
||||
import org.eclipse.paho.mqttv5.client.spi.NetworkModuleFactory;
|
||||
import org.eclipse.paho.mqttv5.common.ExceptionHelper;
|
||||
import org.eclipse.paho.mqttv5.common.MqttException;
|
||||
|
||||
public class WebSocketSecureNetworkModuleFactory implements NetworkModuleFactory {
|
||||
|
||||
@Override
|
||||
public Set<String> getSupportedUriSchemes() {
|
||||
return Collections.unmodifiableSet(new HashSet<>(Arrays.asList("wss")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateURI(URI brokerUri) throws IllegalArgumentException {
|
||||
// so specific requirements so far
|
||||
}
|
||||
|
||||
@Override
|
||||
public NetworkModule createNetworkModule(URI brokerUri, MqttConnectionOptions options, String clientId)
|
||||
throws MqttException
|
||||
{
|
||||
String host = brokerUri.getHost();
|
||||
int port = brokerUri.getPort(); // -1 if not defined
|
||||
if (port == -1) {
|
||||
port = 443;
|
||||
}
|
||||
SocketFactory factory = options.getSocketFactory();
|
||||
SSLSocketFactoryFactory wSSFactoryFactory = null;
|
||||
if (factory == null) {
|
||||
wSSFactoryFactory = new SSLSocketFactoryFactory();
|
||||
Properties sslClientProps = options.getSSLProperties();
|
||||
if (null != sslClientProps) {
|
||||
wSSFactoryFactory.initialize(sslClientProps, null);
|
||||
}
|
||||
factory = wSSFactoryFactory.createSocketFactory(null);
|
||||
|
||||
} else if ((factory instanceof SSLSocketFactory) == false) {
|
||||
throw ExceptionHelper.createMqttException(MqttClientException.REASON_CODE_SOCKET_FACTORY_MISMATCH);
|
||||
}
|
||||
|
||||
// Create the network module...
|
||||
WebSocketSecureNetworkModule netModule = new WebSocketSecureNetworkModule((SSLSocketFactory) factory,
|
||||
brokerUri.toString(), host, port, clientId);
|
||||
netModule.setSSLhandshakeTimeout(options.getConnectionTimeout());
|
||||
netModule.setSSLHostnameVerifier(options.getSSLHostnameVerifier());
|
||||
netModule.setHttpsHostnameVerificationEnabled(options.isHttpsHostnameVerificationEnabled());
|
||||
netModule.setCustomWebSocketHeaders(options.getCustomWebSocketHeaders());
|
||||
// Ciphers suites need to be set, if they are available
|
||||
if (wSSFactoryFactory != null) {
|
||||
String[] enabledCiphers = wSSFactoryFactory.getEnabledCipherSuites(null);
|
||||
if (enabledCiphers != null) {
|
||||
((SSLNetworkModule) netModule).setEnabledCiphers(enabledCiphers);
|
||||
}
|
||||
}
|
||||
return netModule;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user