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/websocket/server | |
parent | 3e23afb0d205e503f6cc7d852e34d07da9a5b7f7 (diff) | |
download | unit-2b8cab1e2478547398ad9c2fe68e025c180cac54.tar.gz unit-2b8cab1e2478547398ad9c2fe68e025c180cac54.tar.bz2 |
Java: introducing websocket support.
Diffstat (limited to 'src/java/javax/websocket/server')
7 files changed, 526 insertions, 0 deletions
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); + } + } +} |