Skip to content

Commit

Permalink
Merge pull request #866 from haruntuncay/master
Browse files Browse the repository at this point in the history
Add PerMessageDeflate Extension support, see #574
  • Loading branch information
marci4 committed Mar 17, 2020
2 parents eacb4f0 + 48a35ce commit 4232021
Show file tree
Hide file tree
Showing 6 changed files with 509 additions and 1 deletion.
72 changes: 72 additions & 0 deletions src/main/example/PerMessageDeflateExample.java
@@ -0,0 +1,72 @@
import org.java_websocket.WebSocket;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft;
import org.java_websocket.drafts.Draft_6455;
import org.java_websocket.extensions.permessage_deflate.PerMessageDeflateExtension;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.handshake.ServerHandshake;
import org.java_websocket.server.WebSocketServer;

import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;

/**
* This class only serves the purpose of showing how to enable PerMessageDeflateExtension for both server and client sockets.<br>
* Extensions are required to be registered in
* @see Draft objects and both
* @see WebSocketClient and
* @see WebSocketServer accept a
* @see Draft object in their constructors.
* This example shows how to achieve it for both server and client sockets.
* Once the connection has been established, PerMessageDeflateExtension will be enabled
* and any messages (binary or text) will be compressed/decompressed automatically.<br>
* Since no additional code is required when sending or receiving messages, this example skips those parts.
*/
public class PerMessageDeflateExample {

private static final Draft perMessageDeflateDraft = new Draft_6455(new PerMessageDeflateExtension());
private static final int PORT = 8887;

private static class DeflateClient extends WebSocketClient {

public DeflateClient() throws URISyntaxException {
super(new URI("ws://localhost:" + PORT), perMessageDeflateDraft);
}

@Override
public void onOpen(ServerHandshake handshakedata) { }

@Override
public void onMessage(String message) { }

@Override
public void onClose(int code, String reason, boolean remote) { }

@Override
public void onError(Exception ex) { }
}

private static class DeflateServer extends WebSocketServer {

public DeflateServer() {
super(new InetSocketAddress(PORT), Collections.singletonList(perMessageDeflateDraft));
}

@Override
public void onOpen(WebSocket conn, ClientHandshake handshake) { }

@Override
public void onClose(WebSocket conn, int code, String reason, boolean remote) { }

@Override
public void onMessage(WebSocket conn, String message) { }

@Override
public void onError(WebSocket conn, Exception ex) { }

@Override
public void onStart() { }
}
}
27 changes: 27 additions & 0 deletions src/main/java/org/java_websocket/drafts/Draft_6455.java
Expand Up @@ -427,6 +427,12 @@ private ByteBuffer createByteBufferFromFramedata( Framedata framedata ) {
byte optcode = fromOpcode( framedata.getOpcode() );
byte one = ( byte ) ( framedata.isFin() ? -128 : 0 );
one |= optcode;
if(framedata.isRSV1())
one |= getRSVByte(1);
if(framedata.isRSV2())
one |= getRSVByte(2);
if(framedata.isRSV3())
one |= getRSVByte(3);
buf.put( one );
byte[] payloadlengthbytes = toByteArray( mes.remaining(), sizebytes );
assert ( payloadlengthbytes.length == sizebytes );
Expand Down Expand Up @@ -585,6 +591,27 @@ private void translateSingleFrameCheckPacketSize(int maxpacketsize, int realpack
}
}

/**
* Get a byte that can set RSV bits when OR(|)'d.
* 0 1 2 3 4 5 6 7
* +-+-+-+-+-------+
* |F|R|R|R| opcode|
* |I|S|S|S| (4) |
* |N|V|V|V| |
* | |1|2|3| |
* @param rsv Can only be {0, 1, 2, 3}
* @return byte that represents which RSV bit is set.
*/
private byte getRSVByte(int rsv){
if(rsv == 1) // 0100 0000
return 0x40;
if(rsv == 2) // 0010 0000
return 0x20;
if(rsv == 3) // 0001 0000
return 0x10;
return 0;
}

/**
* Get the mask byte if existing
* @param mask is mask active or not
Expand Down
@@ -0,0 +1,52 @@
package org.java_websocket.extensions;

import java.util.LinkedHashMap;
import java.util.Map;

public class ExtensionRequestData {

public static String EMPTY_VALUE = "";

private Map<String, String> extensionParameters;
private String extensionName;

private ExtensionRequestData() {
extensionParameters = new LinkedHashMap<String, String>();
}

public static ExtensionRequestData parseExtensionRequest(String extensionRequest) {
ExtensionRequestData extensionData = new ExtensionRequestData();
String[] parts = extensionRequest.split(";");
extensionData.extensionName = parts[0].trim();

for(int i = 1; i < parts.length; i++) {
String[] keyValue = parts[i].split("=");
String value = EMPTY_VALUE;

// Some parameters don't take a value. For those that do, parse the value.
if(keyValue.length > 1) {
String tempValue = keyValue[1].trim();

// If the value is wrapped in quotes, just get the data between them.
if((tempValue.startsWith("\"") && tempValue.endsWith("\""))
|| (tempValue.startsWith("'") && tempValue.endsWith("'"))
&& tempValue.length() > 2)
tempValue = tempValue.substring(1, tempValue.length() - 1);

value = tempValue;
}

extensionData.extensionParameters.put(keyValue[0].trim(), value);
}

return extensionData;
}

public String getExtensionName() {
return extensionName;
}

public Map<String, String> getExtensionParameters() {
return extensionParameters;
}
}

0 comments on commit 4232021

Please sign in to comment.