diff options
author | Max Romanov <max.romanov@nginx.com> | 2019-09-05 15:27:32 +0300 |
---|---|---|
committer | Max Romanov <max.romanov@nginx.com> | 2019-09-05 15:27:32 +0300 |
commit | 2b8cab1e2478547398ad9c2fe68e025c180cac54 (patch) | |
tree | d317fcf9ee52f0f8967116f531784ae533b0ae5a /src/java/javax | |
parent | 3e23afb0d205e503f6cc7d852e34d07da9a5b7f7 (diff) | |
download | unit-2b8cab1e2478547398ad9c2fe68e025c180cac54.tar.gz unit-2b8cab1e2478547398ad9c2fe68e025c180cac54.tar.bz2 |
Java: introducing websocket support.
Diffstat (limited to '')
33 files changed, 2160 insertions, 0 deletions
diff --git a/src/java/javax/websocket/ClientEndpoint.java b/src/java/javax/websocket/ClientEndpoint.java new file mode 100644 index 00000000..ee984171 --- /dev/null +++ b/src/java/javax/websocket/ClientEndpoint.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import javax.websocket.ClientEndpointConfig.Configurator; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface ClientEndpoint { + String[] subprotocols() default {}; + Class<? extends Decoder>[] decoders() default {}; + Class<? extends Encoder>[] encoders() default {}; + public Class<? extends Configurator> configurator() + default Configurator.class; +} diff --git a/src/java/javax/websocket/ClientEndpointConfig.java b/src/java/javax/websocket/ClientEndpointConfig.java new file mode 100644 index 00000000..13b6cba5 --- /dev/null +++ b/src/java/javax/websocket/ClientEndpointConfig.java @@ -0,0 +1,138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public interface ClientEndpointConfig extends EndpointConfig { + + List<String> getPreferredSubprotocols(); + + List<Extension> getExtensions(); + + public Configurator getConfigurator(); + + public final class Builder { + + private static final Configurator DEFAULT_CONFIGURATOR = + new Configurator() {}; + + + public static Builder create() { + return new Builder(); + } + + + private Builder() { + // Hide default constructor + } + + private Configurator configurator = DEFAULT_CONFIGURATOR; + private List<String> preferredSubprotocols = Collections.emptyList(); + private List<Extension> extensions = Collections.emptyList(); + private List<Class<? extends Encoder>> encoders = + Collections.emptyList(); + private List<Class<? extends Decoder>> decoders = + Collections.emptyList(); + + + public ClientEndpointConfig build() { + return new DefaultClientEndpointConfig(preferredSubprotocols, + extensions, encoders, decoders, configurator); + } + + + public Builder configurator(Configurator configurator) { + if (configurator == null) { + this.configurator = DEFAULT_CONFIGURATOR; + } else { + this.configurator = configurator; + } + return this; + } + + + public Builder preferredSubprotocols( + List<String> preferredSubprotocols) { + if (preferredSubprotocols == null || + preferredSubprotocols.size() == 0) { + this.preferredSubprotocols = Collections.emptyList(); + } else { + this.preferredSubprotocols = + Collections.unmodifiableList(preferredSubprotocols); + } + return this; + } + + + public Builder extensions( + List<Extension> extensions) { + if (extensions == null || extensions.size() == 0) { + this.extensions = Collections.emptyList(); + } else { + this.extensions = Collections.unmodifiableList(extensions); + } + return this; + } + + + public Builder encoders(List<Class<? extends Encoder>> encoders) { + if (encoders == null || encoders.size() == 0) { + this.encoders = Collections.emptyList(); + } else { + this.encoders = Collections.unmodifiableList(encoders); + } + return this; + } + + + public Builder decoders(List<Class<? extends Decoder>> decoders) { + if (decoders == null || decoders.size() == 0) { + this.decoders = Collections.emptyList(); + } else { + this.decoders = Collections.unmodifiableList(decoders); + } + return this; + } + } + + + public class Configurator { + + /** + * Provides the client with a mechanism to inspect and/or modify the headers + * that are sent to the server to start the WebSocket handshake. + * + * @param headers The HTTP headers + */ + public void beforeRequest(Map<String, List<String>> headers) { + // NO-OP + } + + /** + * Provides the client with a mechanism to inspect the handshake response + * that is returned from the server. + * + * @param handshakeResponse The response + */ + public void afterResponse(HandshakeResponse handshakeResponse) { + // NO-OP + } + } +} diff --git a/src/java/javax/websocket/CloseReason.java b/src/java/javax/websocket/CloseReason.java new file mode 100644 index 00000000..ef88d135 --- /dev/null +++ b/src/java/javax/websocket/CloseReason.java @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket; + +public class CloseReason { + + private final CloseCode closeCode; + private final String reasonPhrase; + + public CloseReason(CloseReason.CloseCode closeCode, String reasonPhrase) { + this.closeCode = closeCode; + this.reasonPhrase = reasonPhrase; + } + + public CloseCode getCloseCode() { + return closeCode; + } + + public String getReasonPhrase() { + return reasonPhrase; + } + + @Override + public String toString() { + return "CloseReason: code [" + closeCode.getCode() + + "], reason [" + reasonPhrase + "]"; + } + + public interface CloseCode { + int getCode(); + } + + public enum CloseCodes implements CloseReason.CloseCode { + + NORMAL_CLOSURE(1000), + GOING_AWAY(1001), + PROTOCOL_ERROR(1002), + CANNOT_ACCEPT(1003), + RESERVED(1004), + NO_STATUS_CODE(1005), + CLOSED_ABNORMALLY(1006), + NOT_CONSISTENT(1007), + VIOLATED_POLICY(1008), + TOO_BIG(1009), + NO_EXTENSION(1010), + UNEXPECTED_CONDITION(1011), + SERVICE_RESTART(1012), + TRY_AGAIN_LATER(1013), + TLS_HANDSHAKE_FAILURE(1015); + + private int code; + + CloseCodes(int code) { + this.code = code; + } + + public static CloseCode getCloseCode(final int code) { + if (code > 2999 && code < 5000) { + return new CloseCode() { + @Override + public int getCode() { + return code; + } + }; + } + switch (code) { + case 1000: + return CloseCodes.NORMAL_CLOSURE; + case 1001: + return CloseCodes.GOING_AWAY; + case 1002: + return CloseCodes.PROTOCOL_ERROR; + case 1003: + return CloseCodes.CANNOT_ACCEPT; + case 1004: + return CloseCodes.RESERVED; + case 1005: + return CloseCodes.NO_STATUS_CODE; + case 1006: + return CloseCodes.CLOSED_ABNORMALLY; + case 1007: + return CloseCodes.NOT_CONSISTENT; + case 1008: + return CloseCodes.VIOLATED_POLICY; + case 1009: + return CloseCodes.TOO_BIG; + case 1010: + return CloseCodes.NO_EXTENSION; + case 1011: + return CloseCodes.UNEXPECTED_CONDITION; + case 1012: + return CloseCodes.SERVICE_RESTART; + case 1013: + return CloseCodes.TRY_AGAIN_LATER; + case 1015: + return CloseCodes.TLS_HANDSHAKE_FAILURE; + default: + throw new IllegalArgumentException( + "Invalid close code: [" + code + "]"); + } + } + + @Override + public int getCode() { + return code; + } + } +} diff --git a/src/java/javax/websocket/ContainerProvider.java b/src/java/javax/websocket/ContainerProvider.java new file mode 100644 index 00000000..1727ca93 --- /dev/null +++ b/src/java/javax/websocket/ContainerProvider.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket; + +import java.util.Iterator; +import java.util.ServiceLoader; + +/** + * Use the {@link ServiceLoader} mechanism to provide instances of the WebSocket + * client container. + */ +public abstract class ContainerProvider { + + private static final String DEFAULT_PROVIDER_CLASS_NAME = + "nginx.unit.websocket.WsWebSocketContainer"; + + /** + * Create a new container used to create outgoing WebSocket connections. + * + * @return A newly created container. + */ + public static WebSocketContainer getWebSocketContainer() { + WebSocketContainer result = null; + + ServiceLoader<ContainerProvider> serviceLoader = + ServiceLoader.load(ContainerProvider.class); + Iterator<ContainerProvider> iter = serviceLoader.iterator(); + while (result == null && iter.hasNext()) { + result = iter.next().getContainer(); + } + + // Fall-back. Also used by unit tests + if (result == null) { + try { + @SuppressWarnings("unchecked") + Class<WebSocketContainer> clazz = + (Class<WebSocketContainer>) Class.forName( + DEFAULT_PROVIDER_CLASS_NAME); + result = clazz.getConstructor().newInstance(); + } catch (ReflectiveOperationException | IllegalArgumentException | + SecurityException e) { + // No options left. Just return null. + } + } + return result; + } + + protected abstract WebSocketContainer getContainer(); +} diff --git a/src/java/javax/websocket/DecodeException.java b/src/java/javax/websocket/DecodeException.java new file mode 100644 index 00000000..771cfa58 --- /dev/null +++ b/src/java/javax/websocket/DecodeException.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket; + +import java.nio.ByteBuffer; + +public class DecodeException extends Exception { + + private static final long serialVersionUID = 1L; + + private ByteBuffer bb; + private String encodedString; + + public DecodeException(ByteBuffer bb, String message, Throwable cause) { + super(message, cause); + this.bb = bb; + } + + public DecodeException(String encodedString, String message, + Throwable cause) { + super(message, cause); + this.encodedString = encodedString; + } + + public DecodeException(ByteBuffer bb, String message) { + super(message); + this.bb = bb; + } + + public DecodeException(String encodedString, String message) { + super(message); + this.encodedString = encodedString; + } + + public ByteBuffer getBytes() { + return bb; + } + + public String getText() { + return encodedString; + } +} diff --git a/src/java/javax/websocket/Decoder.java b/src/java/javax/websocket/Decoder.java new file mode 100644 index 00000000..fad262e3 --- /dev/null +++ b/src/java/javax/websocket/Decoder.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.nio.ByteBuffer; + +public interface Decoder { + + abstract void init(EndpointConfig endpointConfig); + + abstract void destroy(); + + interface Binary<T> extends Decoder { + + T decode(ByteBuffer bytes) throws DecodeException; + + boolean willDecode(ByteBuffer bytes); + } + + interface BinaryStream<T> extends Decoder { + + T decode(InputStream is) throws DecodeException, IOException; + } + + interface Text<T> extends Decoder { + + T decode(String s) throws DecodeException; + + boolean willDecode(String s); + } + + interface TextStream<T> extends Decoder { + + T decode(Reader reader) throws DecodeException, IOException; + } +} diff --git a/src/java/javax/websocket/DefaultClientEndpointConfig.java b/src/java/javax/websocket/DefaultClientEndpointConfig.java new file mode 100644 index 00000000..ce28cb26 --- /dev/null +++ b/src/java/javax/websocket/DefaultClientEndpointConfig.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +final class DefaultClientEndpointConfig implements ClientEndpointConfig { + + private final List<String> preferredSubprotocols; + private final List<Extension> extensions; + private final List<Class<? extends Encoder>> encoders; + private final List<Class<? extends Decoder>> decoders; + private final Map<String,Object> userProperties = new ConcurrentHashMap<>(); + private final Configurator configurator; + + + DefaultClientEndpointConfig(List<String> preferredSubprotocols, + List<Extension> extensions, + List<Class<? extends Encoder>> encoders, + List<Class<? extends Decoder>> decoders, + Configurator configurator) { + this.preferredSubprotocols = preferredSubprotocols; + this.extensions = extensions; + this.decoders = decoders; + this.encoders = encoders; + this.configurator = configurator; + } + + + @Override + public List<String> getPreferredSubprotocols() { + return preferredSubprotocols; + } + + + @Override + public List<Extension> getExtensions() { + return extensions; + } + + + @Override + public List<Class<? extends Encoder>> getEncoders() { + return encoders; + } + + + @Override + public List<Class<? extends Decoder>> getDecoders() { + return decoders; + } + + + @Override + public final Map<String, Object> getUserProperties() { + return userProperties; + } + + + @Override + public Configurator getConfigurator() { + return configurator; + } +} diff --git a/src/java/javax/websocket/DeploymentException.java b/src/java/javax/websocket/DeploymentException.java new file mode 100644 index 00000000..1678fd09 --- /dev/null +++ b/src/java/javax/websocket/DeploymentException.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket; + +public class DeploymentException extends Exception { + + private static final long serialVersionUID = 1L; + + public DeploymentException(String message) { + super(message); + } + + public DeploymentException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/java/javax/websocket/EncodeException.java b/src/java/javax/websocket/EncodeException.java new file mode 100644 index 00000000..fdb536ac --- /dev/null +++ b/src/java/javax/websocket/EncodeException.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket; + +public class EncodeException extends Exception { + + private static final long serialVersionUID = 1L; + + private Object object; + + public EncodeException(Object object, String message) { + super(message); + this.object = object; + } + + public EncodeException(Object object, String message, Throwable cause) { + super(message, cause); + this.object = object; + } + + public Object getObject() { + return this.object; + } +} diff --git a/src/java/javax/websocket/Encoder.java b/src/java/javax/websocket/Encoder.java new file mode 100644 index 00000000..42a107f0 --- /dev/null +++ b/src/java/javax/websocket/Encoder.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.Writer; +import java.nio.ByteBuffer; + +public interface Encoder { + + abstract void init(EndpointConfig endpointConfig); + + abstract void destroy(); + + interface Text<T> extends Encoder { + + String encode(T object) throws EncodeException; + } + + interface TextStream<T> extends Encoder { + + void encode(T object, Writer writer) + throws EncodeException, IOException; + } + + interface Binary<T> extends Encoder { + + ByteBuffer encode(T object) throws EncodeException; + } + + interface BinaryStream<T> extends Encoder { + + void encode(T object, OutputStream os) + throws EncodeException, IOException; + } +} diff --git a/src/java/javax/websocket/Endpoint.java b/src/java/javax/websocket/Endpoint.java new file mode 100644 index 00000000..9dfdbcce --- /dev/null +++ b/src/java/javax/websocket/Endpoint.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket; + +public abstract class Endpoint { + + /** + * Event that is triggered when a new session starts. + * + * @param session The new session. + * @param config The configuration with which the Endpoint was + * configured. + */ + public abstract void onOpen(Session session, EndpointConfig config); + + /** + * Event that is triggered when a session has closed. + * + * @param session The session + * @param closeReason Why the session was closed + */ + public void onClose(Session session, CloseReason closeReason) { + // NO-OP by default + } + + /** + * Event that is triggered when a protocol error occurs. + * + * @param session The session. + * @param throwable The exception. + */ + public void onError(Session session, Throwable throwable) { + // NO-OP by default + } +} diff --git a/src/java/javax/websocket/EndpointConfig.java b/src/java/javax/websocket/EndpointConfig.java new file mode 100644 index 00000000..0b6c9681 --- /dev/null +++ b/src/java/javax/websocket/EndpointConfig.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket; + +import java.util.List; +import java.util.Map; + +public interface EndpointConfig { + + List<Class<? extends Encoder>> getEncoders(); + + List<Class<? extends Decoder>> getDecoders(); + + Map<String,Object> getUserProperties(); +} diff --git a/src/java/javax/websocket/Extension.java b/src/java/javax/websocket/Extension.java new file mode 100644 index 00000000..b95b27b8 --- /dev/null +++ b/src/java/javax/websocket/Extension.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket; + +import java.util.List; + +public interface Extension { + String getName(); + List<Parameter> getParameters(); + + interface Parameter { + String getName(); + String getValue(); + } +} diff --git a/src/java/javax/websocket/HandshakeResponse.java b/src/java/javax/websocket/HandshakeResponse.java new file mode 100644 index 00000000..807192e8 --- /dev/null +++ b/src/java/javax/websocket/HandshakeResponse.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket; + +import java.util.List; +import java.util.Map; + +public interface HandshakeResponse { + + /** + * Name of the WebSocket accept HTTP header. + */ + public static final String SEC_WEBSOCKET_ACCEPT = "Sec-WebSocket-Accept"; + + Map<String,List<String>> getHeaders(); +} diff --git a/src/java/javax/websocket/MessageHandler.java b/src/java/javax/websocket/MessageHandler.java new file mode 100644 index 00000000..2c30d997 --- /dev/null +++ b/src/java/javax/websocket/MessageHandler.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket; + +public interface MessageHandler { + + interface Partial<T> extends MessageHandler { + + /** + * Called when part of a message is available to be processed. + * + * @param messagePart The message part + * @param last <code>true</code> if this is the last part of + * this message, else <code>false</code> + */ + void onMessage(T messagePart, boolean last); + } + + interface Whole<T> extends MessageHandler { + + /** + * Called when a whole message is available to be processed. + * + * @param message The message + */ + void onMessage(T message); + } +} diff --git a/src/java/javax/websocket/OnClose.java b/src/java/javax/websocket/OnClose.java new file mode 100644 index 00000000..6ee61d36 --- /dev/null +++ b/src/java/javax/websocket/OnClose.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface OnClose { +} diff --git a/src/java/javax/websocket/OnError.java b/src/java/javax/websocket/OnError.java new file mode 100644 index 00000000..ce431484 --- /dev/null +++ b/src/java/javax/websocket/OnError.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface OnError { +} diff --git a/src/java/javax/websocket/OnMessage.java b/src/java/javax/websocket/OnMessage.java new file mode 100644 index 00000000..564fa994 --- /dev/null +++ b/src/java/javax/websocket/OnMessage.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface OnMessage { + long maxMessageSize() default -1; +} diff --git a/src/java/javax/websocket/OnOpen.java b/src/java/javax/websocket/OnOpen.java new file mode 100644 index 00000000..9f0ea6e3 --- /dev/null +++ b/src/java/javax/websocket/OnOpen.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface OnOpen { +} diff --git a/src/java/javax/websocket/PongMessage.java b/src/java/javax/websocket/PongMessage.java new file mode 100644 index 00000000..7e9e3b6a --- /dev/null +++ b/src/java/javax/websocket/PongMessage.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket; + +import java.nio.ByteBuffer; + +/** + * Represents a WebSocket Pong message and used by message handlers to enable + * applications to process the response to any Pings they send. + */ +public interface PongMessage { + /** + * Get the payload of the Pong message. + * + * @return The payload of the Pong message. + */ + ByteBuffer getApplicationData(); +} diff --git a/src/java/javax/websocket/RemoteEndpoint.java b/src/java/javax/websocket/RemoteEndpoint.java new file mode 100644 index 00000000..19c7a100 --- /dev/null +++ b/src/java/javax/websocket/RemoteEndpoint.java @@ -0,0 +1,229 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.Writer; +import java.nio.ByteBuffer; +import java.util.concurrent.Future; + + +public interface RemoteEndpoint { + + interface Async extends RemoteEndpoint { + + /** + * Obtain the timeout (in milliseconds) for sending a message + * asynchronously. The default value is determined by + * {@link WebSocketContainer#getDefaultAsyncSendTimeout()}. + * @return The current send timeout in milliseconds. A non-positive + * value means an infinite timeout. + */ + long getSendTimeout(); + + /** + * Set the timeout (in milliseconds) for sending a message + * asynchronously. The default value is determined by + * {@link WebSocketContainer#getDefaultAsyncSendTimeout()}. + * @param timeout The new timeout for sending messages asynchronously + * in milliseconds. A non-positive value means an + * infinite timeout. + */ + void setSendTimeout(long timeout); + + /** + * Send the message asynchronously, using the SendHandler to signal to the + * client when the message has been sent. + * @param text The text message to send + * @param completion Used to signal to the client when the message has + * been sent + */ + void sendText(String text, SendHandler completion); + + /** + * Send the message asynchronously, using the Future to signal to the + * client when the message has been sent. + * @param text The text message to send + * @return A Future that signals when the message has been sent. + */ + Future<Void> sendText(String text); + + /** + * Send the message asynchronously, using the Future to signal to the client + * when the message has been sent. + * @param data The text message to send + * @return A Future that signals when the message has been sent. + * @throws IllegalArgumentException if {@code data} is {@code null}. + */ + Future<Void> sendBinary(ByteBuffer data); + + /** + * Send the message asynchronously, using the SendHandler to signal to the + * client when the message has been sent. + * @param data The text message to send + * @param completion Used to signal to the client when the message has + * been sent + * @throws IllegalArgumentException if {@code data} or {@code completion} + * is {@code null}. + */ + void sendBinary(ByteBuffer data, SendHandler completion); + + /** + * Encodes object as a message and sends it asynchronously, using the + * Future to signal to the client when the message has been sent. + * @param obj The object to be sent. + * @return A Future that signals when the message has been sent. + * @throws IllegalArgumentException if {@code obj} is {@code null}. + */ + Future<Void> sendObject(Object obj); + + /** + * Encodes object as a message and sends it asynchronously, using the + * SendHandler to signal to the client when the message has been sent. + * @param obj The object to be sent. + * @param completion Used to signal to the client when the message has + * been sent + * @throws IllegalArgumentException if {@code obj} or + * {@code completion} is {@code null}. + */ + void sendObject(Object obj, SendHandler completion); + + } + + interface Basic extends RemoteEndpoint { + + /** + * Send the message, blocking until the message is sent. + * @param text The text message to send. + * @throws IllegalArgumentException if {@code text} is {@code null}. + * @throws IOException if an I/O error occurs during the sending of the + * message. + */ + void sendText(String text) throws IOException; + + /** + * Send the message, blocking until the message is sent. + * @param data The binary message to send + * @throws IllegalArgumentException if {@code data} is {@code null}. + * @throws IOException if an I/O error occurs during the sending of the + * message. + */ + void sendBinary(ByteBuffer data) throws IOException; + + /** + * Sends part of a text message to the remote endpoint. Once the first part + * of a message has been sent, no other text or binary messages may be sent + * until all remaining parts of this message have been sent. + * + * @param fragment The partial message to send + * @param isLast <code>true</code> if this is the last part of the + * message, otherwise <code>false</code> + * @throws IllegalArgumentException if {@code fragment} is {@code null}. + * @throws IOException if an I/O error occurs during the sending of the + * message. + */ + void sendText(String fragment, boolean isLast) throws IOException; + + /** + * Sends part of a binary message to the remote endpoint. Once the first + * part of a message has been sent, no other text or binary messages may be + * sent until all remaining parts of this message have been sent. + * + * @param partialByte The partial message to send + * @param isLast <code>true</code> if this is the last part of the + * message, otherwise <code>false</code> + * @throws IllegalArgumentException if {@code partialByte} is + * {@code null}. + * @throws IOException if an I/O error occurs during the sending of the + * message. + */ + void sendBinary(ByteBuffer partialByte, boolean isLast) throws IOException; + + OutputStream getSendStream() throws IOException; + + Writer getSendWriter() throws IOException; + + /** + * Encodes object as a message and sends it to the remote endpoint. + * @param data The object to be sent. + * @throws EncodeException if there was a problem encoding the + * {@code data} object as a websocket message. + * @throws IllegalArgumentException if {@code data} is {@code null}. + * @throws IOException if an I/O error occurs during the sending of the + * message. + */ + void sendObject(Object data) throws IOException, EncodeException; + + } + /** + * Enable or disable the batching of outgoing messages for this endpoint. If + * batching is disabled when it was previously enabled then this method will + * block until any currently batched messages have been written. + * + * @param batchingAllowed New setting + * @throws IOException If changing the value resulted in a call to + * {@link #flushBatch()} and that call threw an + * {@link IOException}. + */ + void setBatchingAllowed(boolean batchingAllowed) throws IOException; + + /** + * Obtains the current batching status of the endpoint. + * + * @return <code>true</code> if batching is enabled, otherwise + * <code>false</code>. + */ + boolean getBatchingAllowed(); + + /** + * Flush any currently batched messages to the remote endpoint. This method + * will block until the flush completes. + * + * @throws IOException If an I/O error occurs while flushing + */ + void flushBatch() throws IOException; + + /** + * Send a ping message blocking until the message has been sent. Note that + * if a message is in the process of being sent asynchronously, this method + * will block until that message and this ping has been sent. + * + * @param applicationData The payload for the ping message + * + * @throws IOException If an I/O error occurs while sending the ping + * @throws IllegalArgumentException if the applicationData is too large for + * a control message (max 125 bytes) + */ + void sendPing(ByteBuffer applicationData) + throws IOException, IllegalArgumentException; + + /** + * Send a pong message blocking until the message has been sent. Note that + * if a message is in the process of being sent asynchronously, this method + * will block until that message and this pong has been sent. + * + * @param applicationData The payload for the pong message + * + * @throws IOException If an I/O error occurs while sending the pong + * @throws IllegalArgumentException if the applicationData is too large for + * a control message (max 125 bytes) + */ + void sendPong(ByteBuffer applicationData) + throws IOException, IllegalArgumentException; +} + diff --git a/src/java/javax/websocket/SendHandler.java b/src/java/javax/websocket/SendHandler.java new file mode 100644 index 00000000..65b9a19a --- /dev/null +++ b/src/java/javax/websocket/SendHandler.java @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket; + +public interface SendHandler { + + void onResult(SendResult result); +} diff --git a/src/java/javax/websocket/SendResult.java b/src/java/javax/websocket/SendResult.java new file mode 100644 index 00000000..a3797d5b --- /dev/null +++ b/src/java/javax/websocket/SendResult.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket; + +public final class SendResult { + private final Throwable exception; + private final boolean ok; + + public SendResult(Throwable exception) { + this.exception = exception; + this.ok = (exception == null); + } + + public SendResult() { + this (null); + } + + public Throwable getException() { + return exception; + } + + public boolean isOK() { + return ok; + } +} diff --git a/src/java/javax/websocket/Session.java b/src/java/javax/websocket/Session.java new file mode 100644 index 00000000..eea15e5b --- /dev/null +++ b/src/java/javax/websocket/Session.java @@ -0,0 +1,193 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket; + +import java.io.Closeable; +import java.io.IOException; +import java.net.URI; +import java.security.Principal; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public interface Session extends Closeable { + + /** + * Get the container that created this session. + * @return the container that created this session. + */ + WebSocketContainer getContainer(); + + /** + * Registers a {@link MessageHandler} for incoming messages. Only one + * {@link MessageHandler} may be registered for each message type (text, + * binary, pong). The message type will be derived at runtime from the + * provided {@link MessageHandler} instance. It is not always possible to do + * this so it is better to use + * {@link #addMessageHandler(Class, javax.websocket.MessageHandler.Partial)} + * or + * {@link #addMessageHandler(Class, javax.websocket.MessageHandler.Whole)}. + * + * @param handler The message handler for a incoming message + * + * @throws IllegalStateException If a message handler has already been + * registered for the associated message type + */ + void addMessageHandler(MessageHandler handler) throws IllegalStateException; + + Set<MessageHandler> getMessageHandlers(); + + void removeMessageHandler(MessageHandler listener); + + String getProtocolVersion(); + + String getNegotiatedSubprotocol(); + + List<Extension> getNegotiatedExtensions(); + + boolean isSecure(); + + boolean isOpen(); + + /** + * Get the idle timeout for this session. + * @return The current idle timeout for this session in milliseconds. Zero + * or negative values indicate an infinite timeout. + */ + long getMaxIdleTimeout(); + + /** + * Set the idle timeout for this session. + * @param timeout The new idle timeout for this session in milliseconds. + * Zero or negative values indicate an infinite timeout. + */ + void setMaxIdleTimeout(long timeout); + + /** + * Set the current maximum buffer size for binary messages. + * @param max The new maximum buffer size in bytes + */ + void setMaxBinaryMessageBufferSize(int max); + + /** + * Get the current maximum buffer size for binary messages. + * @return The current maximum buffer size in bytes + */ + int getMaxBinaryMessageBufferSize(); + + /** + * Set the maximum buffer size for text messages. + * @param max The new maximum buffer size in characters. + */ + void setMaxTextMessageBufferSize(int max); + + /** + * Get the maximum buffer size for text messages. + * @return The maximum buffer size in characters. + */ + int getMaxTextMessageBufferSize(); + + RemoteEndpoint.Async getAsyncRemote(); + + RemoteEndpoint.Basic getBasicRemote(); + + /** + * Provides a unique identifier for the session. This identifier should not + * be relied upon to be generated from a secure random source. + * @return A unique identifier for the session. + */ + String getId(); + + /** + * Close the connection to the remote end point using the code + * {@link javax.websocket.CloseReason.CloseCodes#NORMAL_CLOSURE} and an + * empty reason phrase. + * + * @throws IOException if an I/O error occurs while the WebSocket session is + * being closed. + */ + @Override + void close() throws IOException; + + + /** + * Close the connection to the remote end point using the specified code + * and reason phrase. + * @param closeReason The reason the WebSocket session is being closed. + * + * @throws IOException if an I/O error occurs while the WebSocket session is + * being closed. + */ + void close(CloseReason closeReason) throws IOException; + + URI getRequestURI(); + + Map<String, List<String>> getRequestParameterMap(); + + String getQueryString(); + + Map<String,String> getPathParameters(); + + Map<String,Object> getUserProperties(); + + Principal getUserPrincipal(); + + /** + * Obtain the set of open sessions associated with the same local endpoint + * as this session. + * + * @return The set of currently open sessions for the local endpoint that + * this session is associated with. + */ + Set<Session> getOpenSessions(); + + /** + * Registers a {@link MessageHandler} for partial incoming messages. Only + * one {@link MessageHandler} may be registered for each message type (text + * or binary, pong messages are never presented as partial messages). + * + * @param <T> The type of message that the given handler is intended + * for + * @param clazz The Class that implements T + * @param handler The message handler for a incoming message + * + * @throws IllegalStateException If a message handler has already been + * registered for the associated message type + * + * @since WebSocket 1.1 + */ + <T> void addMessageHandler(Class<T> clazz, MessageHandler.Partial<T> handler) + throws IllegalStateException; + + /** + * Registers a {@link MessageHandler} for whole incoming messages. Only + * one {@link MessageHandler} may be registered for each message type (text, + * binary, pong). + * + * @param <T> The type of message that the given handler is intended + * for + * @param clazz The Class that implements T + * @param handler The message handler for a incoming message + * + * @throws IllegalStateException If a message handler has already been + * registered for the associated message type + * + * @since WebSocket 1.1 + */ + <T> void addMessageHandler(Class<T> clazz, MessageHandler.Whole<T> handler) + throws IllegalStateException; +} diff --git a/src/java/javax/websocket/SessionException.java b/src/java/javax/websocket/SessionException.java new file mode 100644 index 00000000..428b82ec --- /dev/null +++ b/src/java/javax/websocket/SessionException.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket; + +public class SessionException extends Exception { + + private static final long serialVersionUID = 1L; + + private final Session session; + + + public SessionException(String message, Throwable cause, Session session) { + super(message, cause); + this.session = session; + } + + + public Session getSession() { + return session; + } +} diff --git a/src/java/javax/websocket/WebSocketContainer.java b/src/java/javax/websocket/WebSocketContainer.java new file mode 100644 index 00000000..f2da3e43 --- /dev/null +++ b/src/java/javax/websocket/WebSocketContainer.java @@ -0,0 +1,131 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket; + +import java.io.IOException; +import java.net.URI; +import java.util.Set; + +public interface WebSocketContainer { + + /** + * Get the default timeout for sending a message asynchronously. + * @return The current default timeout in milliseconds. A non-positive value + * means an infinite timeout. + */ + long getDefaultAsyncSendTimeout(); + + /** + * Set the default timeout for sending a message asynchronously. + * @param timeout The new default timeout in milliseconds. A non-positive + * value means an infinite timeout. + */ + void setAsyncSendTimeout(long timeout); + + Session connectToServer(Object endpoint, URI path) + throws DeploymentException, IOException; + + Session connectToServer(Class<?> annotatedEndpointClass, URI path) + throws DeploymentException, IOException; + + /** + * Creates a new connection to the WebSocket. + * + * @param endpoint + * The endpoint instance that will handle responses from the + * server + * @param clientEndpointConfiguration + * Used to configure the new connection + * @param path + * The full URL of the WebSocket endpoint to connect to + * + * @return The WebSocket session for the connection + * + * @throws DeploymentException If the connection cannot be established + * @throws IOException If an I/O occurred while trying to establish the + * connection + */ + Session connectToServer(Endpoint endpoint, + ClientEndpointConfig clientEndpointConfiguration, URI path) + throws DeploymentException, IOException; + + /** + * Creates a new connection to the WebSocket. + * + * @param endpoint + * An instance of this class will be created to handle responses + * from the server + * @param clientEndpointConfiguration + * Used to configure the new connection + * @param path + * The full URL of the WebSocket endpoint to connect to + * + * @return The WebSocket session for the connection + * + * @throws DeploymentException If the connection cannot be established + * @throws IOException If an I/O occurred while trying to establish the + * connection + */ + Session connectToServer(Class<? extends Endpoint> endpoint, + ClientEndpointConfig clientEndpointConfiguration, URI path) + throws DeploymentException, IOException; + + /** + * Get the current default session idle timeout. + * @return The current default session idle timeout in milliseconds. Zero or + * negative values indicate an infinite timeout. + */ + long getDefaultMaxSessionIdleTimeout(); + + /** + * Set the default session idle timeout. + * @param timeout The new default session idle timeout in milliseconds. Zero + * or negative values indicate an infinite timeout. + */ + void setDefaultMaxSessionIdleTimeout(long timeout); + + /** + * Get the default maximum buffer size for binary messages. + * @return The current default maximum buffer size in bytes + */ + int getDefaultMaxBinaryMessageBufferSize(); + + /** + * Set the default maximum buffer size for binary messages. + * @param max The new default maximum buffer size in bytes + */ + void setDefaultMaxBinaryMessageBufferSize(int max); + + /** + * Get the default maximum buffer size for text messages. + * @return The current default maximum buffer size in characters + */ + int getDefaultMaxTextMessageBufferSize(); + + /** + * Set the default maximum buffer size for text messages. + * @param max The new default maximum buffer size in characters + */ + void setDefaultMaxTextMessageBufferSize(int max); + + /** + * Get the installed extensions. + * @return The set of extensions that are supported by this WebSocket + * implementation. + */ + Set<Extension> getInstalledExtensions(); +} diff --git a/src/java/javax/websocket/server/DefaultServerEndpointConfig.java b/src/java/javax/websocket/server/DefaultServerEndpointConfig.java new file mode 100644 index 00000000..7c3b8d7d --- /dev/null +++ b/src/java/javax/websocket/server/DefaultServerEndpointConfig.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket.server; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import javax.websocket.Decoder; +import javax.websocket.Encoder; +import javax.websocket.Extension; + +/** + * Provides the default configuration for WebSocket server endpoints. + */ +final class DefaultServerEndpointConfig implements ServerEndpointConfig { + + private final Class<?> endpointClass; + private final String path; + private final List<String> subprotocols; + private final List<Extension> extensions; + private final List<Class<? extends Encoder>> encoders; + private final List<Class<? extends Decoder>> decoders; + private final Configurator serverEndpointConfigurator; + private final Map<String,Object> userProperties = new ConcurrentHashMap<>(); + + DefaultServerEndpointConfig( + Class<?> endpointClass, String path, + List<String> subprotocols, List<Extension> extensions, + List<Class<? extends Encoder>> encoders, + List<Class<? extends Decoder>> decoders, + Configurator serverEndpointConfigurator) { + this.endpointClass = endpointClass; + this.path = path; + this.subprotocols = subprotocols; + this.extensions = extensions; + this.encoders = encoders; + this.decoders = decoders; + this.serverEndpointConfigurator = serverEndpointConfigurator; + } + + @Override + public Class<?> getEndpointClass() { + return endpointClass; + } + + @Override + public List<Class<? extends Encoder>> getEncoders() { + return this.encoders; + } + + @Override + public List<Class<? extends Decoder>> getDecoders() { + return this.decoders; + } + + @Override + public String getPath() { + return path; + } + + @Override + public Configurator getConfigurator() { + return serverEndpointConfigurator; + } + + @Override + public final Map<String, Object> getUserProperties() { + return userProperties; + } + + @Override + public final List<String> getSubprotocols() { + return subprotocols; + } + + @Override + public final List<Extension> getExtensions() { + return extensions; + } +} diff --git a/src/java/javax/websocket/server/HandshakeRequest.java b/src/java/javax/websocket/server/HandshakeRequest.java new file mode 100644 index 00000000..f2e33273 --- /dev/null +++ b/src/java/javax/websocket/server/HandshakeRequest.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket.server; + +import java.net.URI; +import java.security.Principal; +import java.util.List; +import java.util.Map; + +/** + * Represents the HTTP request that asked to be upgraded to WebSocket. + */ +public interface HandshakeRequest { + + static final String SEC_WEBSOCKET_KEY = "Sec-WebSocket-Key"; + static final String SEC_WEBSOCKET_PROTOCOL = "Sec-WebSocket-Protocol"; + static final String SEC_WEBSOCKET_VERSION = "Sec-WebSocket-Version"; + static final String SEC_WEBSOCKET_EXTENSIONS= "Sec-WebSocket-Extensions"; + + Map<String,List<String>> getHeaders(); + + Principal getUserPrincipal(); + + URI getRequestURI(); + + boolean isUserInRole(String role); + + /** + * Get the HTTP Session object associated with this request. Object is used + * to avoid a direct dependency on the Servlet API. + * @return The javax.servlet.http.HttpSession object associated with this + * request, if any. + */ + Object getHttpSession(); + + Map<String, List<String>> getParameterMap(); + + String getQueryString(); +} diff --git a/src/java/javax/websocket/server/PathParam.java b/src/java/javax/websocket/server/PathParam.java new file mode 100644 index 00000000..ff1d085e --- /dev/null +++ b/src/java/javax/websocket/server/PathParam.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket.server; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Used to annotate method parameters on POJO endpoints the the {@link + * ServerEndpoint} has been defined with a {@link ServerEndpoint#value()} that + * uses a URI template. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface PathParam { + String value(); +} diff --git a/src/java/javax/websocket/server/ServerApplicationConfig.java b/src/java/javax/websocket/server/ServerApplicationConfig.java new file mode 100644 index 00000000..b91f1c43 --- /dev/null +++ b/src/java/javax/websocket/server/ServerApplicationConfig.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket.server; + +import java.util.Set; + +import javax.websocket.Endpoint; + +/** + * Applications may provide an implementation of this interface to filter the + * discovered WebSocket endpoints that are deployed. Implementations of this + * class will be discovered via an ServletContainerInitializer scan. + */ +public interface ServerApplicationConfig { + + /** + * Enables applications to filter the discovered implementations of + * {@link ServerEndpointConfig}. + * + * @param scanned The {@link Endpoint} implementations found in the + * application + * @return The set of configurations for the endpoint the application + * wishes to deploy + */ + Set<ServerEndpointConfig> getEndpointConfigs( + Set<Class<? extends Endpoint>> scanned); + + /** + * Enables applications to filter the discovered classes annotated with + * {@link ServerEndpoint}. + * + * @param scanned The POJOs annotated with {@link ServerEndpoint} found in + * the application + * @return The set of POJOs the application wishes to deploy + */ + Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> scanned); +} diff --git a/src/java/javax/websocket/server/ServerContainer.java b/src/java/javax/websocket/server/ServerContainer.java new file mode 100644 index 00000000..3243a07c --- /dev/null +++ b/src/java/javax/websocket/server/ServerContainer.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket.server; + +import javax.websocket.DeploymentException; +import javax.websocket.WebSocketContainer; + +/** + * Provides the ability to deploy endpoints programmatically. + */ +public interface ServerContainer extends WebSocketContainer { + public abstract void addEndpoint(Class<?> clazz) throws DeploymentException; + + public abstract void addEndpoint(ServerEndpointConfig sec) + throws DeploymentException; +} diff --git a/src/java/javax/websocket/server/ServerEndpoint.java b/src/java/javax/websocket/server/ServerEndpoint.java new file mode 100644 index 00000000..43b7dfa2 --- /dev/null +++ b/src/java/javax/websocket/server/ServerEndpoint.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket.server; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import javax.websocket.Decoder; +import javax.websocket.Encoder; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface ServerEndpoint { + + /** + * URI or URI-template that the annotated class should be mapped to. + * @return The URI or URI-template that the annotated class should be mapped + * to. + */ + String value(); + + String[] subprotocols() default {}; + + Class<? extends Decoder>[] decoders() default {}; + + Class<? extends Encoder>[] encoders() default {}; + + public Class<? extends ServerEndpointConfig.Configurator> configurator() + default ServerEndpointConfig.Configurator.class; +} diff --git a/src/java/javax/websocket/server/ServerEndpointConfig.java b/src/java/javax/websocket/server/ServerEndpointConfig.java new file mode 100644 index 00000000..5afdf79c --- /dev/null +++ b/src/java/javax/websocket/server/ServerEndpointConfig.java @@ -0,0 +1,218 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF 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 javax.websocket.server; + +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.ServiceLoader; + +import javax.websocket.Decoder; +import javax.websocket.Encoder; +import javax.websocket.EndpointConfig; +import javax.websocket.Extension; +import javax.websocket.HandshakeResponse; + +/** + * Provides configuration information for WebSocket endpoints published to a + * server. Applications may provide their own implementation or use + * {@link Builder}. + */ +public interface ServerEndpointConfig extends EndpointConfig { + + Class<?> getEndpointClass(); + + /** + * Returns the path at which this WebSocket server endpoint has been + * registered. It may be a path or a level 0 URI template. + * @return The registered path + */ + String getPath(); + + List<String> getSubprotocols(); + + List<Extension> getExtensions(); + + Configurator getConfigurator(); + + + public final class Builder { + + public static Builder create( + Class<?> endpointClass, String path) { + return new Builder(endpointClass, path); + } + + + private final Class<?> endpointClass; + private final String path; + private List<Class<? extends Encoder>> encoders = + Collections.emptyList(); + private List<Class<? extends Decoder>> decoders = + Collections.emptyList(); + private List<String> subprotocols = Collections.emptyList(); + private List<Extension> extensions = Collections.emptyList(); + private Configurator configurator = + Configurator.fetchContainerDefaultConfigurator(); + + + private Builder(Class<?> endpointClass, + String path) { + this.endpointClass = endpointClass; + this.path = path; + } + + public ServerEndpointConfig build() { + return new DefaultServerEndpointConfig(endpointClass, path, + subprotocols, extensions, encoders, decoders, configurator); + } + + + public Builder encoders( + List<Class<? extends Encoder>> encoders) { + if (encoders == null || encoders.size() == 0) { + this.encoders = Collections.emptyList(); + } else { + this.encoders = Collections.unmodifiableList(encoders); + } + return this; + } + + + public Builder decoders( + List<Class<? extends Decoder>> decoders) { + if (decoders == null || decoders.size() == 0) { + this.decoders = Collections.emptyList(); + } else { + this.decoders = Collections.unmodifiableList(decoders); + } + return this; + } + + + public Builder subprotocols( + List<String> subprotocols) { + if (subprotocols == null || subprotocols.size() == 0) { + this.subprotocols = Collections.emptyList(); + } else { + this.subprotocols = Collections.unmodifiableList(subprotocols); + } + return this; + } + + + public Builder extensions( + List<Extension> extensions) { + if (extensions == null || extensions.size() == 0) { + this.extensions = Collections.emptyList(); + } else { + this.extensions = Collections.unmodifiableList(extensions); + } + return this; + } + + + public Builder configurator(Configurator serverEndpointConfigurator) { + if (serverEndpointConfigurator == null) { + this.configurator = Configurator.fetchContainerDefaultConfigurator(); + } else { + this.configurator = serverEndpointConfigurator; + } + return this; + } + } + + + public class Configurator { + + private static volatile Configurator defaultImpl = null; + private static final Object defaultImplLock = new Object(); + + private static final String DEFAULT_IMPL_CLASSNAME = + "nginx.unit.websocket.server.DefaultServerEndpointConfigurator"; + + public static void setDefault(Configurator def) { + synchronized (defaultImplLock) { + defaultImpl = def; + } + } + + static Configurator fetchContainerDefaultConfigurator() { + if (defaultImpl == null) { + synchronized (defaultImplLock) { + if (defaultImpl == null) { + defaultImpl = loadDefault(); + } + } + } + return defaultImpl; + } + + + private static Configurator loadDefault() { + Configurator result = null; + + ServiceLoader<Configurator> serviceLoader = + ServiceLoader.load(Configurator.class); + + Iterator<Configurator> iter = serviceLoader.iterator(); + while (result == null && iter.hasNext()) { + result = iter.next(); + } + + // Fall-back. Also used by unit tests + if (result == null) { + try { + @SuppressWarnings("unchecked") + Class<Configurator> clazz = + (Class<Configurator>) Class.forName( + DEFAULT_IMPL_CLASSNAME); + result = clazz.getConstructor().newInstance(); + } catch (ReflectiveOperationException | IllegalArgumentException | + SecurityException e) { + // No options left. Just return null. + } + } + return result; + } + + public String getNegotiatedSubprotocol(List<String> supported, + List<String> requested) { + return fetchContainerDefaultConfigurator().getNegotiatedSubprotocol(supported, requested); + } + + public List<Extension> getNegotiatedExtensions(List<Extension> installed, + List<Extension> requested) { + return fetchContainerDefaultConfigurator().getNegotiatedExtensions(installed, requested); + } + + public boolean checkOrigin(String originHeaderValue) { + return fetchContainerDefaultConfigurator().checkOrigin(originHeaderValue); + } + + public void modifyHandshake(ServerEndpointConfig sec, + HandshakeRequest request, HandshakeResponse response) { + fetchContainerDefaultConfigurator().modifyHandshake(sec, request, response); + } + + public <T extends Object> T getEndpointInstance(Class<T> clazz) + throws InstantiationException { + return fetchContainerDefaultConfigurator().getEndpointInstance( + clazz); + } + } +} |