001/** 002 * 003 * Copyright 2020 Aditya Borikar, 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.rce; 018 019import java.io.IOException; 020import java.net.URI; 021import java.net.URISyntaxException; 022import java.util.ArrayList; 023import java.util.Collections; 024import java.util.List; 025 026import org.jivesoftware.smack.altconnections.HttpLookupMethod; 027import org.jivesoftware.smack.altconnections.HttpLookupMethod.LinkRelation; 028import org.jivesoftware.smack.util.rce.RemoteConnectionEndpointLookupFailure; 029import org.jivesoftware.smack.xml.XmlPullParserException; 030 031import org.jxmpp.jid.DomainBareJid; 032 033public final class WebSocketRemoteConnectionEndpointLookup { 034 035 public static Result lookup(DomainBareJid domainBareJid) { 036 List<RemoteConnectionEndpointLookupFailure> lookupFailures = new ArrayList<>(1); 037 038 List<URI> rcUriList = null; 039 try { 040 // Look for remote connection endpoints by making use of http lookup method described inside XEP-0156. 041 rcUriList = HttpLookupMethod.lookup(domainBareJid, 042 LinkRelation.WEBSOCKET); 043 } catch (IOException | XmlPullParserException | URISyntaxException e) { 044 lookupFailures.add(new RemoteConnectionEndpointLookupFailure.HttpLookupFailure( 045 domainBareJid, e)); 046 return new Result(lookupFailures); 047 } 048 049 List<SecureWebSocketRemoteConnectionEndpoint> discoveredSecureEndpoints = new ArrayList<>(rcUriList.size()); 050 List<InsecureWebSocketRemoteConnectionEndpoint> discoveredInsecureEndpoints = new ArrayList<>(rcUriList.size()); 051 052 for (URI webSocketUri : rcUriList) { 053 WebSocketRemoteConnectionEndpoint wsRce = WebSocketRemoteConnectionEndpoint.from(webSocketUri); 054 if (wsRce instanceof SecureWebSocketRemoteConnectionEndpoint) { 055 SecureWebSocketRemoteConnectionEndpoint secureWsRce = (SecureWebSocketRemoteConnectionEndpoint) wsRce; 056 discoveredSecureEndpoints.add(secureWsRce); 057 } else if (wsRce instanceof InsecureWebSocketRemoteConnectionEndpoint) { 058 InsecureWebSocketRemoteConnectionEndpoint insecureWsRce = (InsecureWebSocketRemoteConnectionEndpoint) wsRce; 059 discoveredInsecureEndpoints.add(insecureWsRce); 060 } else { 061 // WebSocketRemoteConnectionEndpoint.from() must return an instance which type is one of the above. 062 throw new AssertionError(); 063 } 064 } 065 066 return new Result(discoveredSecureEndpoints, discoveredInsecureEndpoints, lookupFailures); 067 } 068 069 public static final class Result { 070 public final List<SecureWebSocketRemoteConnectionEndpoint> discoveredSecureEndpoints; 071 public final List<InsecureWebSocketRemoteConnectionEndpoint> discoveredInsecureEndpoints; 072 public final List<RemoteConnectionEndpointLookupFailure> lookupFailures; 073 074 public Result() { 075 this(Collections.emptyList()); 076 } 077 078 public Result(List<RemoteConnectionEndpointLookupFailure> lookupFailures) { 079 // The list of endpoints needs to be mutable, because maybe a user supplied endpoint will be added to it. 080 // Hence, we do not use Collections.emptyList() as argument for the discovered endpoints. 081 this(new ArrayList<>(1), new ArrayList<>(1), lookupFailures); 082 } 083 084 public Result(List<SecureWebSocketRemoteConnectionEndpoint> discoveredSecureEndpoints, 085 List<InsecureWebSocketRemoteConnectionEndpoint> discoveredInsecureEndpoints, 086 List<RemoteConnectionEndpointLookupFailure> lookupFailures) { 087 this.discoveredSecureEndpoints = discoveredSecureEndpoints; 088 this.discoveredInsecureEndpoints = discoveredInsecureEndpoints; 089 this.lookupFailures = lookupFailures; 090 } 091 092 public boolean isEmpty() { 093 return discoveredSecureEndpoints.isEmpty() && discoveredInsecureEndpoints.isEmpty(); 094 } 095 096 public int discoveredEndpointCount() { 097 return discoveredSecureEndpoints.size() + discoveredInsecureEndpoints.size(); 098 } 099 100 // TODO: Remove the following methods since the fields are already public? Or make the fields private and use 101 // the methods? I tend to remove the methods, as their method name is pretty long. But OTOH the fields reference 102 // mutable datastructures, which is uncommon to be public. 103 public List<SecureWebSocketRemoteConnectionEndpoint> getDiscoveredSecureRemoteConnectionEndpoints() { 104 return discoveredSecureEndpoints; 105 } 106 107 public List<InsecureWebSocketRemoteConnectionEndpoint> getDiscoveredInsecureRemoteConnectionEndpoints() { 108 return discoveredInsecureEndpoints; 109 } 110 111 public List<RemoteConnectionEndpointLookupFailure> getLookupFailures() { 112 return lookupFailures; 113 } 114 } 115}