/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2014 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.mod_cluster;

import static org.xnio.IoUtils.safeClose;

import java.util.concurrent.TimeUnit;

import io.undertow.client.ClientConnection;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.ServerConnection;
import io.undertow.server.handlers.proxy.ExclusivityChecker;
import io.undertow.server.handlers.proxy.ProxyCallback;
import io.undertow.server.handlers.proxy.ProxyClient;
import io.undertow.server.handlers.proxy.ProxyConnection;
import io.undertow.util.AttachmentKey;

class ModClusterProxyClient implements ProxyClient {

    
The attachment key that is used to attach the proxy connection to the exchange.

This cannot be static as otherwise a connection from a different client could be re-used.

/** * The attachment key that is used to attach the proxy connection to the exchange. * <p> * This cannot be static as otherwise a connection from a different client could be re-used. */
private final AttachmentKey<ExclusiveConnectionHolder> exclusiveConnectionKey = AttachmentKey .create(ExclusiveConnectionHolder.class); private final ExclusivityChecker exclusivityChecker; private final ModClusterContainer container; protected ModClusterProxyClient(ExclusivityChecker exclusivityChecker, ModClusterContainer container) { this.exclusivityChecker = exclusivityChecker; this.container = container; } @Override public ProxyTarget findTarget(HttpServerExchange exchange) { return container.findTarget(exchange); } public void getConnection(final ProxyTarget target, final HttpServerExchange exchange, final ProxyCallback<ProxyConnection> callback, final long timeout, final TimeUnit timeUnit) { final ExclusiveConnectionHolder holder = exchange.getConnection().getAttachment(exclusiveConnectionKey); if (holder != null && holder.connection.getConnection().isOpen()) { // Something has already caused an exclusive connection to be // allocated so keep using it. callback.completed(exchange, holder.connection); return; } if (! (target instanceof ModClusterProxyTarget)) { callback.couldNotResolveBackend(exchange); return; } // Resolve the node final ModClusterProxyTarget proxyTarget = (ModClusterProxyTarget) target; final Context context = proxyTarget.resolveContext(exchange); if (context == null) { callback.couldNotResolveBackend(exchange); } else { if (holder != null || (exclusivityChecker != null && exclusivityChecker.isExclusivityRequired(exchange))) { // If we have a holder, even if the connection was closed we now // exclusivity was already requested so our client // may be assuming it still exists. final ProxyCallback<ProxyConnection> wrappedCallback = new ProxyCallback<ProxyConnection>() { @Override public void completed(HttpServerExchange exchange, ProxyConnection result) { if (holder != null) { holder.connection = result; } else { final ExclusiveConnectionHolder newHolder = new ExclusiveConnectionHolder(); newHolder.connection = result; ServerConnection connection = exchange.getConnection(); connection.putAttachment(exclusiveConnectionKey, newHolder); connection.addCloseListener(new ServerConnection.CloseListener() { @Override public void closed(ServerConnection connection) { ClientConnection clientConnection = newHolder.connection.getConnection(); if (clientConnection.isOpen()) { safeClose(clientConnection); } } }); } callback.completed(exchange, result); } @Override public void queuedRequestFailed(HttpServerExchange exchange) { callback.queuedRequestFailed(exchange); } @Override public void failed(HttpServerExchange exchange) { callback.failed(exchange); } @Override public void couldNotResolveBackend(HttpServerExchange exchange) { callback.couldNotResolveBackend(exchange); } }; context.handleRequest(proxyTarget, exchange, wrappedCallback, timeout, timeUnit, true); } else { context.handleRequest(proxyTarget, exchange, callback, timeout, timeUnit, false); } } } private static class ExclusiveConnectionHolder { private ProxyConnection connection; } }