Skip to content

Commit

Permalink
Make websocket message types clearer
Browse files Browse the repository at this point in the history
  • Loading branch information
shs96c committed Feb 4, 2020
1 parent 2a962b7 commit 177754a
Show file tree
Hide file tree
Showing 11 changed files with 280 additions and 47 deletions.
44 changes: 44 additions & 0 deletions java/client/src/org/openqa/selenium/remote/http/BinaryMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package org.openqa.selenium.remote.http;

import java.nio.ByteBuffer;
import java.util.Objects;

public class BinaryMessage implements Message {

private final byte[] data;

public BinaryMessage(ByteBuffer data) {
Objects.requireNonNull(data, "Data to use must be set.");

ByteBuffer copy = data.asReadOnlyBuffer();
this.data = new byte[copy.capacity()];
copy.get(this.data);
}

public BinaryMessage(byte[] data) {
this.data = Objects.requireNonNull(data, "Data to use must be set.");
}

public byte[] data() {
return data;
}

}

42 changes: 42 additions & 0 deletions java/client/src/org/openqa/selenium/remote/http/CloseMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package org.openqa.selenium.remote.http;

public class CloseMessage implements Message {

private final int code;
private final String reason;

public CloseMessage(int code) {
this(code, "");
}

public CloseMessage(int code, String reason) {
this.code = code;
this.reason = reason == null ? "" : reason;
}

public int code() {
return code;
}

public String reason() {
return reason;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package org.openqa.selenium.remote.http;

import org.openqa.selenium.WebDriverException;

public class ConnectionFailedException extends WebDriverException {

public ConnectionFailedException(String message) {
super(message);
}

public ConnectionFailedException(String message, Throwable cause) {
super(message, cause);
}

}
21 changes: 21 additions & 0 deletions java/client/src/org/openqa/selenium/remote/http/Message.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package org.openqa.selenium.remote.http;

public interface Message {
}
33 changes: 33 additions & 0 deletions java/client/src/org/openqa/selenium/remote/http/TextMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package org.openqa.selenium.remote.http;

import java.util.Objects;

public class TextMessage implements Message {

private final String text;

public TextMessage(CharSequence text) {
this.text = Objects.requireNonNull(text, "Message text must be set.").toString();
}

public String text() {
return text;
}
}
27 changes: 23 additions & 4 deletions java/client/src/org/openqa/selenium/remote/http/WebSocket.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,44 @@

public interface WebSocket extends Closeable {

WebSocket sendText(CharSequence data);
WebSocket send(Message message);

default WebSocket sendText(CharSequence data) {
return send(new TextMessage(data));
}

@Override
void close();

void abort();

class Listener {
public void onText(CharSequence data) {
// Does nothing

public void onMessage(Message message) {
if (message instanceof BinaryMessage) {
onBinary(((BinaryMessage) message).data());
} else if (message instanceof CloseMessage) {
onClose(((CloseMessage) message).code(), ((CloseMessage) message).reason());
} else if (message instanceof TextMessage) {
onText(((TextMessage) message).text());
}
}

public void onError(Throwable cause) {
public void onBinary(byte[] data) {
// Does nothing
}

public void onClose(int code, String reason) {
// Does nothing
}

public void onText(CharSequence data) {
// Does nothing
}

public void onError(Throwable cause) {
// Does nothing
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,18 @@
package org.openqa.selenium.remote.http.netty;

import org.asynchttpclient.AsyncHttpClient;
import org.asynchttpclient.ListenableFuture;
import org.asynchttpclient.ws.WebSocketListener;
import org.asynchttpclient.ws.WebSocketUpgradeHandler;
import org.openqa.selenium.remote.http.BinaryMessage;
import org.openqa.selenium.remote.http.ClientConfig;
import org.openqa.selenium.remote.http.CloseMessage;
import org.openqa.selenium.remote.http.ConnectionFailedException;
import org.openqa.selenium.remote.http.Filter;
import org.openqa.selenium.remote.http.HttpRequest;
import org.openqa.selenium.remote.http.HttpResponse;
import org.openqa.selenium.remote.http.Message;
import org.openqa.selenium.remote.http.TextMessage;
import org.openqa.selenium.remote.http.WebSocket;

import java.net.MalformedURLException;
Expand Down Expand Up @@ -51,35 +57,43 @@ private NettyWebSocket(AsyncHttpClient client, org.asynchttpclient.Request reque
try {
URL origUrl = new URL(request.getUrl());
URI wsUri = new URI("ws", null, origUrl.getHost(), origUrl.getPort(), origUrl.getPath(), null, null);
socket = client.prepareGet(wsUri.toString())
.execute(new WebSocketUpgradeHandler.Builder()
.addWebSocketListener(new WebSocketListener() {
@Override
public void onOpen(org.asynchttpclient.ws.WebSocket websocket) {
}

@Override
public void onClose(org.asynchttpclient.ws.WebSocket websocket, int code, String reason) {
listener.onClose(code, reason);
}

@Override
public void onError(Throwable t) {
listener.onError(t);
}

@Override
public void onTextFrame(String payload, boolean finalFragment, int rsv) {
if (payload != null) {
listener.onText(payload);
}
}
}).build()).get();
ListenableFuture<org.asynchttpclient.netty.ws.NettyWebSocket> future = client.prepareGet(wsUri.toString())
.execute(new WebSocketUpgradeHandler.Builder()
.addWebSocketListener(new WebSocketListener() {
@Override
public void onOpen(org.asynchttpclient.ws.WebSocket websocket) {
}

@Override
public void onClose(org.asynchttpclient.ws.WebSocket websocket, int code, String reason) {
listener.onClose(code, reason);
}

@Override
public void onError(Throwable t) {
listener.onError(t);
}

@Override
public void onTextFrame(String payload, boolean finalFragment, int rsv) {
if (payload != null) {
listener.onText(payload);
}
}
}).build());
socket = future.toCompletableFuture()
.exceptionally(t -> {t.printStackTrace(); return null;})
.get();

if (socket == null) {
throw new ConnectionFailedException("Unable to establish websocket connection to " + request.getUrl());
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.log(Level.WARNING, "NettyWebSocket initial request interrupted", e);
throw new ConnectionFailedException("NettyWebSocket initial request interrupted", e);
} catch (ExecutionException | MalformedURLException | URISyntaxException e) {
throw new RuntimeException("NettyWebSocket initial request execution error", e);
throw new ConnectionFailedException("NettyWebSocket initial request execution error", e);
}
}

Expand All @@ -105,6 +119,19 @@ static BiFunction<HttpRequest, Listener, WebSocket> create(ClientConfig config)
};
}

@Override
public WebSocket send(Message message) {
if (message instanceof BinaryMessage) {
socket.sendBinaryFrame(((BinaryMessage) message).data());
} else if (message instanceof CloseMessage) {
socket.sendCloseFrame(((CloseMessage) message).code(), ((CloseMessage) message).reason());
} else if (message instanceof TextMessage) {
socket.sendTextFrame(((TextMessage) message).text());
}

return this;
}

@Override
public WebSocket sendText(CharSequence data) {
socket.sendTextFrame(data.toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ java_library(
"//java/client/src/org/openqa/selenium/remote/http",
artifact("com.google.guava:guava"),
artifact("com.squareup.okhttp3:okhttp"),
artifact("com.squareup.okio:okio"),
],
)
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,15 @@
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocketListener;
import okio.ByteString;
import org.openqa.selenium.remote.http.BinaryMessage;
import org.openqa.selenium.remote.http.ClientConfig;
import org.openqa.selenium.remote.http.CloseMessage;
import org.openqa.selenium.remote.http.Filter;
import org.openqa.selenium.remote.http.HttpRequest;
import org.openqa.selenium.remote.http.HttpResponse;
import org.openqa.selenium.remote.http.Message;
import org.openqa.selenium.remote.http.TextMessage;
import org.openqa.selenium.remote.http.WebSocket;

import java.util.Objects;
Expand Down Expand Up @@ -84,8 +89,16 @@ static BiFunction<HttpRequest, WebSocket.Listener, WebSocket> create(ClientConfi
}

@Override
public WebSocket sendText(CharSequence data) {
socket.send(data.toString());
public WebSocket send(Message message) {
if (message instanceof BinaryMessage) {
byte[] data = ((BinaryMessage) message).data();
socket.send(ByteString.of(data, 0, data.length));
} else if (message instanceof CloseMessage) {
socket.close(((CloseMessage) message).code(), ((CloseMessage) message).reason());
} else if (message instanceof TextMessage) {
socket.send(((TextMessage) message).text());
}

return this;
}

Expand Down
3 changes: 2 additions & 1 deletion java/maven_deps.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ def selenium_java_deps():
"com.google.auto:auto-common:0.10",
"com.google.auto.service:auto-service:1.0-rc6",
"com.google.auto.service:auto-service-annotations:1.0-rc6",
"com.squareup.okhttp3:okhttp:4.3.0",
"com.squareup.okhttp3:okhttp:4.3.1",
"com.squareup.okio:okio:2.4.1",
"com.typesafe.netty:netty-reactive-streams:2.0.4",
"io.lettuce:lettuce-core:5.2.1.RELEASE",
"io.netty:netty-buffer:%s" % netty_version,
Expand Down
Loading

0 comments on commit 177754a

Please sign in to comment.