001/** 002 * 003 * Copyright 2020 Aditya Borikar, 2020-2021 Florian Schmaus 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smack.websocket; 018 019import java.net.URI; 020import java.net.URISyntaxException; 021import java.util.HashSet; 022import java.util.Set; 023 024import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode; 025import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection; 026import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionConfiguration; 027import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionModule; 028import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionModuleDescriptor; 029import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal; 030import org.jivesoftware.smack.fsm.StateDescriptor; 031import org.jivesoftware.smack.util.Objects; 032import org.jivesoftware.smack.websocket.XmppWebSocketTransportModule.EstablishingWebSocketConnectionStateDescriptor; 033import org.jivesoftware.smack.websocket.impl.WebSocketFactory; 034import org.jivesoftware.smack.websocket.rce.WebSocketRemoteConnectionEndpoint; 035 036/** 037 * The descriptor class for {@link XmppWebSocketTransportModule}. 038 * <br> 039 * To add {@link XmppWebSocketTransportModule} to {@link ModularXmppClientToServerConnection}, 040 * use {@link ModularXmppClientToServerConnectionConfiguration.Builder#addModule(ModularXmppClientToServerConnectionModuleDescriptor)}. 041 */ 042public final class XmppWebSocketTransportModuleDescriptor extends ModularXmppClientToServerConnectionModuleDescriptor { 043 private final boolean performWebSocketEndpointDiscovery; 044 private final boolean implicitWebSocketEndpoint; 045 private final WebSocketRemoteConnectionEndpoint wsRce; 046 047 final WebSocketFactory webSocketFactory; 048 049 public XmppWebSocketTransportModuleDescriptor(Builder builder) { 050 this.performWebSocketEndpointDiscovery = builder.performWebSocketEndpointDiscovery; 051 this.implicitWebSocketEndpoint = builder.implicitWebSocketEndpoint; 052 this.webSocketFactory = builder.webSocketFactory; 053 054 URI uri = builder.uri; 055 if (uri != null) { 056 wsRce = WebSocketRemoteConnectionEndpoint.from(uri); 057 } else { 058 wsRce = null; 059 } 060 } 061 062 @Override 063 @SuppressWarnings({"incomplete-switch", "MissingCasesInEnumSwitch"}) 064 protected void validateConfiguration(ModularXmppClientToServerConnectionConfiguration configuration) { 065 if (wsRce == null) { 066 return; 067 } 068 069 SecurityMode securityMode = configuration.getSecurityMode(); 070 switch (securityMode) { 071 case required: 072 if (!wsRce.isSecureEndpoint()) { 073 throw new IllegalArgumentException("The provided WebSocket endpoint " + wsRce + " is not a secure endpoint, but the connection configuration requires secure endpoints"); 074 } 075 break; 076 case disabled: 077 if (wsRce.isSecureEndpoint()) { 078 throw new IllegalArgumentException("The provided WebSocket endpoint " + wsRce + " is a secure endpoint, but the connection configuration has security disabled"); 079 } 080 break; 081 } 082 } 083 084 /** 085 * Returns true if websocket endpoint discovery is true, returns false otherwise. 086 * @return boolean 087 */ 088 public boolean isWebSocketEndpointDiscoveryEnabled() { 089 return performWebSocketEndpointDiscovery; 090 } 091 092 public boolean isImplicitWebSocketEndpointEnabled() { 093 return implicitWebSocketEndpoint; 094 } 095 096 /** 097 * Returns explicitly configured websocket endpoint uri. 098 * @return uri 099 */ 100 public URI getExplicitlyProvidedUri() { 101 return wsRce.getUri(); 102 } 103 104 WebSocketRemoteConnectionEndpoint getExplicitlyProvidedEndpoint() { 105 return wsRce; 106 } 107 108 @Override 109 protected Set<Class<? extends StateDescriptor>> getStateDescriptors() { 110 Set<Class<? extends StateDescriptor>> res = new HashSet<>(); 111 res.add(EstablishingWebSocketConnectionStateDescriptor.class); 112 return res; 113 } 114 115 @Override 116 protected ModularXmppClientToServerConnectionModule<? extends ModularXmppClientToServerConnectionModuleDescriptor> constructXmppConnectionModule( 117 ModularXmppClientToServerConnectionInternal connectionInternal) { 118 return new XmppWebSocketTransportModule(this, connectionInternal); 119 } 120 121 /** 122 * Returns a new instance of {@link Builder}. 123 * <br> 124 * @return Builder 125 * @param connectionConfigurationBuilder {@link ModularXmppClientToServerConnectionConfiguration.Builder}. 126 */ 127 public static Builder getBuilder( 128 ModularXmppClientToServerConnectionConfiguration.Builder connectionConfigurationBuilder) { 129 return new Builder(connectionConfigurationBuilder); 130 } 131 132 /** 133 * Builder class for {@link XmppWebSocketTransportModuleDescriptor}. 134 * <br> 135 * To obtain an instance of {@link XmppWebSocketTransportModuleDescriptor.Builder}, use {@link XmppWebSocketTransportModuleDescriptor#getBuilder(ModularXmppClientToServerConnectionConfiguration.Builder)} method. 136 * <br> 137 * Use {@link Builder#explicitlySetWebSocketEndpoint(URI)} to configure the URI of an endpoint as a backup in case connection couldn't be established with endpoints through http lookup. 138 * <br> 139 * Use {@link Builder#explicitlySetWebSocketEndpointAndDiscovery(URI, boolean)} to configure endpoint and disallow websocket endpoint discovery through http lookup. 140 * By default, {@link Builder#performWebSocketEndpointDiscovery} is set to true. 141 * <br> 142 * Use {@link Builder#build()} to obtain {@link XmppWebSocketTransportModuleDescriptor}. 143 */ 144 public static final class Builder extends ModularXmppClientToServerConnectionModuleDescriptor.Builder { 145 private boolean performWebSocketEndpointDiscovery = true; 146 private boolean implicitWebSocketEndpoint = true; 147 private URI uri; 148 private WebSocketFactory webSocketFactory; 149 150 private Builder( 151 ModularXmppClientToServerConnectionConfiguration.Builder connectionConfigurationBuilder) { 152 super(connectionConfigurationBuilder); 153 } 154 155 public Builder explicitlySetWebSocketEndpoint(URI endpoint) { 156 return explicitlySetWebSocketEndpointAndDiscovery(endpoint, true); 157 } 158 159 public Builder explicitlySetWebSocketEndpointAndDiscovery(URI endpoint, boolean performWebSocketEndpointDiscovery) { 160 Objects.requireNonNull(endpoint, "Provided endpoint URI must not be null"); 161 this.uri = endpoint; 162 this.performWebSocketEndpointDiscovery = performWebSocketEndpointDiscovery; 163 return this; 164 } 165 166 public Builder explicitlySetWebSocketEndpoint(CharSequence endpoint) throws URISyntaxException { 167 URI endpointUri = new URI(endpoint.toString()); 168 return explicitlySetWebSocketEndpoint(endpointUri); 169 } 170 171 public Builder explicitlySetWebSocketEndpointAndDiscovery(CharSequence endpoint, boolean performWebSocketEndpointDiscovery) 172 throws URISyntaxException { 173 URI endpointUri = new URI(endpoint.toString()); 174 return explicitlySetWebSocketEndpointAndDiscovery(endpointUri, performWebSocketEndpointDiscovery); 175 } 176 177 public Builder disableImplicitWebsocketEndpoint() { 178 implicitWebSocketEndpoint = false; 179 return this; 180 } 181 182 public Builder setWebSocketFactory(WebSocketFactory webSocketFactory) { 183 Objects.requireNonNull(webSocketFactory); 184 this.webSocketFactory = webSocketFactory; 185 return this; 186 } 187 188 @Override 189 public ModularXmppClientToServerConnectionModuleDescriptor build() { 190 return new XmppWebSocketTransportModuleDescriptor(this); 191 } 192 } 193}