Add websocket-resources as a module

This commit is contained in:
Moxie Marlinspike
2019-04-30 23:58:27 -07:00
parent 66917cd2c0
commit 9220f4d829
38 changed files with 9206 additions and 26 deletions

View File

@@ -0,0 +1,27 @@
/**
* Copyright (C) 2014 Open WhisperSystems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whispersystems.websocket.messages;
public class InvalidMessageException extends Exception {
public InvalidMessageException(String s) {
super(s);
}
public InvalidMessageException(Exception e) {
super(e);
}
}

View File

@@ -0,0 +1,32 @@
/**
* Copyright (C) 2014 Open WhisperSystems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whispersystems.websocket.messages;
public interface WebSocketMessage {
public enum Type {
UNKNOWN_MESSAGE,
REQUEST_MESSAGE,
RESPONSE_MESSAGE
}
public Type getType();
public WebSocketRequestMessage getRequestMessage();
public WebSocketResponseMessage getResponseMessage();
public byte[] toByteArray();
}

View File

@@ -0,0 +1,38 @@
/**
* Copyright (C) 2014 Open WhisperSystems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whispersystems.websocket.messages;
import java.util.List;
import java.util.Optional;
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public interface WebSocketMessageFactory {
public WebSocketMessage parseMessage(byte[] serialized, int offset, int len)
throws InvalidMessageException;
public WebSocketMessage createRequest(Optional<Long> requestId,
String verb, String path,
List<String> headers,
Optional<byte[]> body);
public WebSocketMessage createResponse(long requestId, int status, String message,
List<String> headers,
Optional<byte[]> body);
}

View File

@@ -0,0 +1,33 @@
/**
* Copyright (C) 2014 Open WhisperSystems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whispersystems.websocket.messages;
import java.util.Map;
import java.util.Optional;
public interface WebSocketRequestMessage {
public String getVerb();
public String getPath();
public Map<String,String> getHeaders();
public Optional<byte[]> getBody();
public long getRequestId();
public boolean hasRequestId();
}

View File

@@ -0,0 +1,29 @@
/**
* Copyright (C) 2014 Open WhisperSystems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whispersystems.websocket.messages;
import java.util.Map;
import java.util.Optional;
public interface WebSocketResponseMessage {
public long getRequestId();
public int getStatus();
public String getMessage();
public Map<String,String> getHeaders();
public Optional<byte[]> getBody();
}

View File

@@ -0,0 +1,81 @@
/**
* Copyright (C) 2014 Open WhisperSystems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whispersystems.websocket.messages.protobuf;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import org.whispersystems.websocket.messages.InvalidMessageException;
import org.whispersystems.websocket.messages.WebSocketMessage;
import org.whispersystems.websocket.messages.WebSocketRequestMessage;
import org.whispersystems.websocket.messages.WebSocketResponseMessage;
public class ProtobufWebSocketMessage implements WebSocketMessage {
private final SubProtocol.WebSocketMessage message;
ProtobufWebSocketMessage(byte[] buffer, int offset, int length) throws InvalidMessageException {
try {
this.message = SubProtocol.WebSocketMessage.parseFrom(ByteString.copyFrom(buffer, offset, length));
if (getType() == Type.REQUEST_MESSAGE) {
if (!message.getRequest().hasVerb() || !message.getRequest().hasPath()) {
throw new InvalidMessageException("Missing required request attributes!");
}
} else if (getType() == Type.RESPONSE_MESSAGE) {
if (!message.getResponse().hasId() || !message.getResponse().hasStatus() || !message.getResponse().hasMessage()) {
throw new InvalidMessageException("Missing required response attributes!");
}
}
} catch (InvalidProtocolBufferException e) {
throw new InvalidMessageException(e);
}
}
ProtobufWebSocketMessage(SubProtocol.WebSocketMessage message) {
this.message = message;
}
@Override
public Type getType() {
if (message.getType().getNumber() == SubProtocol.WebSocketMessage.Type.REQUEST_VALUE &&
message.hasRequest())
{
return Type.REQUEST_MESSAGE;
} else if (message.getType().getNumber() == SubProtocol.WebSocketMessage.Type.RESPONSE_VALUE &&
message.hasResponse())
{
return Type.RESPONSE_MESSAGE;
} else {
return Type.UNKNOWN_MESSAGE;
}
}
@Override
public WebSocketRequestMessage getRequestMessage() {
return new ProtobufWebSocketRequestMessage(message.getRequest());
}
@Override
public WebSocketResponseMessage getResponseMessage() {
return new ProtobufWebSocketResponseMessage(message.getResponse());
}
@Override
public byte[] toByteArray() {
return message.toByteArray();
}
}

View File

@@ -0,0 +1,93 @@
/**
* Copyright (C) 2014 Open WhisperSystems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whispersystems.websocket.messages.protobuf;
import com.google.protobuf.ByteString;
import org.whispersystems.websocket.messages.InvalidMessageException;
import org.whispersystems.websocket.messages.WebSocketMessage;
import org.whispersystems.websocket.messages.WebSocketMessageFactory;
import java.util.List;
import java.util.Optional;
public class ProtobufWebSocketMessageFactory implements WebSocketMessageFactory {
@Override
public WebSocketMessage parseMessage(byte[] serialized, int offset, int len)
throws InvalidMessageException
{
return new ProtobufWebSocketMessage(serialized, offset, len);
}
@Override
public WebSocketMessage createRequest(Optional<Long> requestId,
String verb, String path,
List<String> headers,
Optional<byte[]> body)
{
SubProtocol.WebSocketRequestMessage.Builder requestMessage =
SubProtocol.WebSocketRequestMessage.newBuilder()
.setVerb(verb)
.setPath(path);
if (requestId.isPresent()) {
requestMessage.setId(requestId.get());
}
if (body.isPresent()) {
requestMessage.setBody(ByteString.copyFrom(body.get()));
}
if (headers != null) {
requestMessage.addAllHeaders(headers);
}
SubProtocol.WebSocketMessage message
= SubProtocol.WebSocketMessage.newBuilder()
.setType(SubProtocol.WebSocketMessage.Type.REQUEST)
.setRequest(requestMessage)
.build();
return new ProtobufWebSocketMessage(message);
}
@Override
public WebSocketMessage createResponse(long requestId, int status, String messageString, List<String> headers, Optional<byte[]> body) {
SubProtocol.WebSocketResponseMessage.Builder responseMessage =
SubProtocol.WebSocketResponseMessage.newBuilder()
.setId(requestId)
.setStatus(status)
.setMessage(messageString);
if (body.isPresent()) {
responseMessage.setBody(ByteString.copyFrom(body.get()));
}
if (headers != null) {
responseMessage.addAllHeaders(headers);
}
SubProtocol.WebSocketMessage message =
SubProtocol.WebSocketMessage.newBuilder()
.setType(SubProtocol.WebSocketMessage.Type.RESPONSE)
.setResponse(responseMessage)
.build();
return new ProtobufWebSocketMessage(message);
}
}

View File

@@ -0,0 +1,76 @@
/**
* Copyright (C) 2014 Open WhisperSystems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whispersystems.websocket.messages.protobuf;
import org.whispersystems.websocket.messages.WebSocketRequestMessage;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
public class ProtobufWebSocketRequestMessage implements WebSocketRequestMessage {
private final SubProtocol.WebSocketRequestMessage message;
ProtobufWebSocketRequestMessage(SubProtocol.WebSocketRequestMessage message) {
this.message = message;
}
@Override
public String getVerb() {
return message.getVerb();
}
@Override
public String getPath() {
return message.getPath();
}
@Override
public Optional<byte[]> getBody() {
if (message.hasBody()) {
return Optional.of(message.getBody().toByteArray());
} else {
return Optional.empty();
}
}
@Override
public long getRequestId() {
return message.getId();
}
@Override
public boolean hasRequestId() {
return message.hasId();
}
@Override
public Map<String, String> getHeaders() {
Map<String, String> results = new HashMap<>();
for (String header : message.getHeadersList()) {
String[] tokenized = header.split(":");
if (tokenized.length == 2 && tokenized[0] != null && tokenized[1] != null) {
results.put(tokenized[0].trim().toLowerCase(), tokenized[1].trim());
}
}
return results;
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2014 Open WhisperSystems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whispersystems.websocket.messages.protobuf;
import org.whispersystems.websocket.messages.WebSocketResponseMessage;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
public class ProtobufWebSocketResponseMessage implements WebSocketResponseMessage {
private final SubProtocol.WebSocketResponseMessage message;
public ProtobufWebSocketResponseMessage(SubProtocol.WebSocketResponseMessage message) {
this.message = message;
}
@Override
public long getRequestId() {
return message.getId();
}
@Override
public int getStatus() {
return message.getStatus();
}
@Override
public String getMessage() {
return message.getMessage();
}
@Override
public Optional<byte[]> getBody() {
if (message.hasBody()) {
return Optional.of(message.getBody().toByteArray());
} else {
return Optional.empty();
}
}
@Override
public Map<String, String> getHeaders() {
Map<String, String> results = new HashMap<>();
for (String header : message.getHeadersList()) {
String[] tokenized = header.split(":");
if (tokenized.length == 2 && tokenized[0] != null && tokenized[1] != null) {
results.put(tokenized[0].trim().toLowerCase(), tokenized[1].trim());
}
}
return results;
}
}