/** * Derive the host and port from virtual address if virtual address is indeed contains the actual * host and port of the server. This is the final resort to compute the final URI in {@link * #computeFinalUriWithLoadBalancer(ClientRequest)} if there is no load balancer available and the * request URI is incomplete. Sub classes can override this method to be more accurate or throws * ClientException if it does not want to support virtual address to be the same as physical * server address. * * <p>The virtual address is used by certain load balancers to filter the servers of the same * function to form the server pool. */ protected Pair<String, Integer> deriveHostAndPortFromVipAddress(String vipAddress) throws URISyntaxException, ClientException { Pair<String, Integer> hostAndPort = new Pair<String, Integer>(null, -1); URI uri = new URI(vipAddress); String scheme = uri.getScheme(); if (scheme == null) { uri = new URI("http://" + vipAddress); } String host = uri.getHost(); if (host == null) { throw new ClientException("Unable to derive host/port from vip address " + vipAddress); } int port = uri.getPort(); if (port < 0) { port = getDefaultPort(); } if (port < 0) { throw new ClientException("Unable to derive host/port from vip address " + vipAddress); } hostAndPort.setFirst(host); hostAndPort.setSecond(port); return hostAndPort; }
/** * Compute the final URI from a partial URI in the request. The following steps are performed: * <li>if host is missing and there is a load balancer, get the host/port from server chosen from * load balancer * <li>if host is missing and there is no load balancer, try to derive host/port from virtual * address set with the client * <li>if host is present and the authority part of the URI is a virtual address set for the * client, and there is a load balancer, get the host/port from server chosen from load * balancer * <li>if host is present but none of the above applies, interpret the host as the actual physical * address * <li>if host is missing but none of the above applies, throws ClientException * * @param original Original URI passed from caller * @return new request with the final URI */ protected S computeFinalUriWithLoadBalancer(S original) throws ClientException { URI newURI; URI theUrl = original.getUri(); if (theUrl == null) { throw new ClientException(ClientException.ErrorType.GENERAL, "NULL URL passed in"); } String host = theUrl.getHost(); Pair<String, Integer> schemeAndPort = deriveSchemeAndPortFromPartialUri(original); String scheme = schemeAndPort.first(); int port = schemeAndPort.second(); // Various Supported Cases // The loadbalancer to use and the instances it has is based on how it was registered // In each of these cases, the client might come in using Full Url or Partial URL ILoadBalancer lb = getLoadBalancer(); Object loadBalancerKey = original.getLoadBalancerKey(); if (host == null) { // Partial URL Case // well we have to just get the right instances from lb - or we fall back if (lb != null) { Server svc = lb.chooseServer(loadBalancerKey); if (svc == null) { throw new ClientException( ClientException.ErrorType.GENERAL, "LoadBalancer returned null Server for :" + clientName); } host = svc.getHost(); port = svc.getPort(); if (host == null) { throw new ClientException( ClientException.ErrorType.GENERAL, "Invalid Server for :" + svc); } if (logger.isDebugEnabled()) { logger.debug(clientName + " using LB returned Server:" + svc + "for request:" + theUrl); } } else { // No Full URL - and we dont have a LoadBalancer registered to // obtain a server // if we have a vipAddress that came with the registration, we // can use that else we // bail out if (vipAddresses != null && vipAddresses.contains(",")) { throw new ClientException( ClientException.ErrorType.GENERAL, this.clientName + "Partial URI of (" + theUrl + ") has been sent in to RestClient (with no LB) to be executed." + " Also, there are multiple vipAddresses and hence RestClient cant pick" + "one vipAddress to complete this partial uri"); } else if (vipAddresses != null) { try { Pair<String, Integer> hostAndPort = deriveHostAndPortFromVipAddress(vipAddresses); host = hostAndPort.first(); port = hostAndPort.second(); } catch (URISyntaxException e) { throw new ClientException( ClientException.ErrorType.GENERAL, this.clientName + "Partial URI of (" + theUrl + ") has been sent in to RestClient (with no LB) to be executed." + " Also, the configured/registered vipAddress is unparseable (to determine host and port)"); } } else { throw new ClientException( ClientException.ErrorType.GENERAL, this.clientName + " has no LoadBalancer registered and passed in a partial URL request (with no host:port)." + " Also has no vipAddress registered"); } } } else { // Full URL Case // This could either be a vipAddress or a hostAndPort or a real DNS // if vipAddress or hostAndPort, we just have to consult the loadbalancer // but if it does not return a server, we should just proceed anyways // and assume its a DNS // For restClients registered using a vipAddress AND executing a request // by passing in the full URL (including host and port), we should only // consult lb IFF the URL passed is registered as vipAddress in Discovery boolean shouldInterpretAsVip = false; if (lb != null) { shouldInterpretAsVip = isVipRecognized(original.getUri().getAuthority()); } if (shouldInterpretAsVip) { Server svc = lb.chooseServer(loadBalancerKey); if (svc != null) { host = svc.getHost(); port = svc.getPort(); if (host == null) { throw new ClientException( ClientException.ErrorType.GENERAL, "Invalid Server for :" + svc); } if (logger.isDebugEnabled()) { logger.debug("using LB returned Server:" + svc + "for request:" + theUrl); } } else { // just fall back as real DNS if (logger.isDebugEnabled()) { logger.debug( host + ":" + port + " assumed to be a valid VIP address or exists in the DNS"); } } } else { // consult LB to obtain vipAddress backed instance given full URL // Full URL execute request - where url!=vipAddress if (logger.isDebugEnabled()) { logger.debug("Using full URL passed in by caller (not using LB/Discovery):" + theUrl); } } } // end of creating final URL if (host == null) { throw new ClientException( ClientException.ErrorType.GENERAL, "Request contains no HOST to talk to"); } // just verify that at this point we have a full URL try { String urlPath = ""; if (theUrl.getRawPath() != null && theUrl.getRawPath().startsWith("/")) { urlPath = theUrl.getRawPath(); } else { urlPath = "/" + theUrl.getRawPath(); } newURI = new URI( scheme, theUrl.getUserInfo(), host, port, urlPath, theUrl.getQuery(), theUrl.getFragment()); return (S) original.replaceUri(newURI); } catch (URISyntaxException e) { throw new ClientException(ClientException.ErrorType.GENERAL, e.getMessage()); } }