/*
* JBoss, Home of Professional Open Source.
* Copyright 2019 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed 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 io.undertow.server.handlers.proxy;
import java.nio.CharBuffer;
import java.util.Iterator;
import java.util.NoSuchElementException;
Factory for route/affinity iterator parser. This implementation lazily parses routes while supporting strategies in RouteParsingStrategy
including ranked routing. The iterator never creates new String instances but returns a CharSequence wrapper from the existing session ID. Author: Radoslav Husar
/**
* Factory for route/affinity iterator parser. This implementation lazily parses routes while supporting strategies in
* {@link RouteParsingStrategy} including ranked routing. The iterator never creates new String instances but returns
* a CharSequence wrapper from the existing session ID.
*
* @author Radoslav Husar
*/
public class RouteIteratorFactory {
public enum ParsingCompatibility {
MOD_JK,
MOD_CLUSTER,
}
private final RouteParsingStrategy routeParsingStrategy;
private final ParsingCompatibility parsingCompatibility;
private final String rankedRouteDelimiter;
Params: - routeParsingStrategy – route parsing strategy
- parsingCompatibility – route parsing compatibility behavior
/**
* @param routeParsingStrategy route parsing strategy
* @param parsingCompatibility route parsing compatibility behavior
*/
public RouteIteratorFactory(RouteParsingStrategy routeParsingStrategy, ParsingCompatibility parsingCompatibility) {
this(routeParsingStrategy, parsingCompatibility, null);
}
Params: - routeParsingStrategy – route parsing strategy
- parsingCompatibility – route parsing compatibility behavior
- rankedRouteDelimiter – String sequence to split routes at if ranked routing is enabled
/**
* @param routeParsingStrategy route parsing strategy
* @param parsingCompatibility route parsing compatibility behavior
* @param rankedRouteDelimiter String sequence to split routes at if ranked routing is enabled
*/
public RouteIteratorFactory(RouteParsingStrategy routeParsingStrategy, ParsingCompatibility parsingCompatibility, String rankedRouteDelimiter) {
if (routeParsingStrategy == RouteParsingStrategy.RANKED && rankedRouteDelimiter == null) {
throw new IllegalArgumentException();
}
this.routeParsingStrategy = routeParsingStrategy;
this.parsingCompatibility = parsingCompatibility;
this.rankedRouteDelimiter = rankedRouteDelimiter;
}
Returns an Iterator<CharSequence>
of routes. Params: - sessionId – String of sessionID from the cookie/parameter possibly including encoded/appended affinity/route information
Returns: routes iterator; never returns null
/**
* Returns an {@link Iterator<CharSequence>} of routes.
*
* @param sessionId String of sessionID from the cookie/parameter possibly including encoded/appended affinity/route information
* @return routes iterator; never returns {@code null}
*/
public Iterator<CharSequence> iterator(String sessionId) {
return new RouteIterator(sessionId);
}
private class RouteIterator implements Iterator<CharSequence> {
private final String sessionId;
private boolean nextResolved;
private int nextPos;
private CharSequence next;
RouteIterator(String sessionId) {
this.sessionId = sessionId;
if (routeParsingStrategy == RouteParsingStrategy.NONE) {
this.nextResolved = true;
this.next = null;
} else {
int index = (sessionId == null) ? -1 : sessionId.indexOf('.');
if (index == -1) {
// Case where there is no routing information at all.
this.nextResolved = true;
this.next = null;
} else {
nextPos = index + 1;
}
}
}
@Override
public boolean hasNext() {
resolveNext();
return next != null;
}
@Override
public CharSequence next() {
resolveNext();
if (next != null) {
CharSequence result = next;
nextResolved = (routeParsingStrategy != RouteParsingStrategy.RANKED);
next = null;
return result;
}
throw new NoSuchElementException();
}
private void resolveNext() {
if (!nextResolved) {
if (routeParsingStrategy != RouteParsingStrategy.RANKED) {
if (parsingCompatibility == ParsingCompatibility.MOD_JK) {
// mod_jk aka LoadBalancingProxyClient uses mod_jk parsing mechanism though never supports domain
// it treats route only as the sequence after the first "." but before the second ".";
// i.e. does not allow "." in route
int last = sessionId.indexOf('.', nextPos);
if (last == -1) {
last = sessionId.length();
}
next = CharBuffer.wrap(sessionId, nextPos, last);
} else {
// mod_cluster treats everything after first "." as the route; i.e. allows "." in route
next = CharBuffer.wrap(sessionId, nextPos, sessionId.length());
}
} else if (nextPos >= sessionId.length()) {
next = null;
} else {
int currentPos = sessionId.indexOf(rankedRouteDelimiter, nextPos);
next = CharBuffer.wrap(sessionId, nextPos, (currentPos != -1) ? currentPos : sessionId.length());
nextPos += next.length() + rankedRouteDelimiter.length();
}
nextResolved = true;
}
}
}
}