blob: 4c16520828fdad4945b4b0a828db6e18e3017acc [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.components.devtools_bridge;
import android.util.JsonReader;
import android.util.JsonWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
/**
* Defines protocol of control channel of SessionBase. Messages are JSON serializable
* and transferred through AbstractDataChannel.
*/
public final class SessionControlMessages {
private SessionControlMessages() {
throw new RuntimeException("Class not intended to instantiate");
}
/**
* Types of messages that client sends to server.
*/
enum ClientMessageType {
UNKNOWN_REQUEST
}
/**
* Types of messages that servers sends to client.
*/
enum ServerMessageType {
ICE_EXCHANGE,
UNKNOWN_RESPONSE
}
/**
* Base class for all messages.
*/
public abstract static class Message<T extends Enum> {
public final T type;
protected Message(T type) {
this.type = type;
}
public void write(JsonWriter writer) throws IOException {
writer.name("type");
writer.value(type.toString());
}
}
/**
* Base calss for messages that client sends to server.
*/
public abstract static class ClientMessage extends Message<ClientMessageType> {
protected ClientMessage(ClientMessageType type) {
super(type);
}
}
/**
* Base class for messages that server sends to client.
*/
public abstract static class ServerMessage extends Message<ServerMessageType> {
protected ServerMessage(ServerMessageType type) {
super(type);
}
}
/**
* Server sends this message when it has ICE candidates to exchange. Client initiates
* ICE exchange over signaling channel.
*/
public static final class IceExchangeMessage extends ServerMessage {
public IceExchangeMessage() {
super(ServerMessageType.ICE_EXCHANGE);
}
}
/**
* Server response on unrecognized client message.
*/
public static final class UnknownResponseMessage extends ServerMessage {
public final String rawRequestType;
public UnknownResponseMessage(String rawRequestType) {
super(ServerMessageType.UNKNOWN_RESPONSE);
this.rawRequestType = rawRequestType;
}
@Override
public void write(JsonWriter writer) throws IOException {
super.write(writer);
writer.name("rawRequestType");
writer.value(rawRequestType.toString());
}
}
/**
* Helper class to represent message of unknown type. Should not be sent.
*/
public static final class UnknownRequestMessage extends ClientMessage {
public final String rawType;
public UnknownRequestMessage(String rawType) {
super(ClientMessageType.UNKNOWN_REQUEST);
this.rawType = rawType;
}
@Override
public void write(JsonWriter writer) throws IOException {
throw new RuntimeException("Should not be serialized");
}
public UnknownResponseMessage createResponse() {
return new UnknownResponseMessage(rawType);
}
}
private static <T extends Enum<T>> T getMessageType(
Class<T> enumType, String rawType, T defaultType) throws IOException {
try {
return Enum.valueOf(enumType, rawType);
} catch (IllegalArgumentException e) {
if (defaultType != null)
return defaultType;
throw new IOException("Invalid message type " + rawType);
}
}
public static void write(JsonWriter writer, Message<?> message) throws IOException {
writer.beginObject();
message.write(writer);
writer.endObject();
}
public static ClientMessage readClientMessage(JsonReader reader) throws IOException {
String rawType = "";
boolean success = false;
reader.beginObject();
while (reader.hasNext()) {
String name = reader.nextName();
if ("type".equals(name)) {
rawType = reader.nextString();
}
}
reader.endObject();
switch (getMessageType(ClientMessageType.class,
rawType,
ClientMessageType.UNKNOWN_REQUEST)) {
case UNKNOWN_REQUEST:
return new UnknownRequestMessage(rawType);
}
throw new IOException("Invalid message");
}
public static ServerMessage readServerMessage(JsonReader reader) throws IOException {
String rawType = "";
String rawRequestType = null;
reader.beginObject();
while (reader.hasNext()) {
String name = reader.nextName();
if ("type".equals(name)) {
rawType = reader.nextString();
} else if ("rawRequestType".equals(name)) {
rawRequestType = reader.nextString();
}
}
reader.endObject();
switch (getMessageType(ServerMessageType.class, rawType, null)) {
case ICE_EXCHANGE:
return new IceExchangeMessage();
case UNKNOWN_RESPONSE:
return new UnknownResponseMessage(rawRequestType);
}
throw new IOException("Invalid message");
}
/**
* Base class for client and server message handlers.
*/
public abstract static class MessageHandler {
protected abstract void readMessage(JsonReader reader) throws IOException;
public boolean readMessage(byte[] bytes) throws InvalidFormatException {
try {
readMessage(new JsonReader(new InputStreamReader(new ByteArrayInputStream(bytes))));
return true;
} catch (IOException e) {
throw new InvalidFormatException(e);
}
}
}
/**
* Exception when parsing or handling message.
*/
public static class InvalidFormatException extends IOException {
public InvalidFormatException(IOException e) {
super(e);
}
public InvalidFormatException(String message) {
super(message);
}
}
/**
* Base class for handler of server messages (to be created on client).
*/
public abstract static class ServerMessageHandler extends MessageHandler {
@Override
protected void readMessage(JsonReader reader) throws IOException {
onMessage(readServerMessage(reader));
}
protected abstract void onMessage(ServerMessage message);
}
/**
* Base class for handler of client messages (to be created on server).
*/
public abstract static class ClientMessageHandler extends MessageHandler {
@Override
public void readMessage(JsonReader reader) throws IOException {
onMessage(readClientMessage(reader));
}
protected abstract void onMessage(ClientMessage message);
}
public static byte[] toByteArray(Message<?> message) {
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
JsonWriter writer = new JsonWriter(new OutputStreamWriter(byteStream));
try {
write(writer, message);
writer.close();
return byteStream.toByteArray();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}