blob: 100fa8a7e82f417553732e21a962a018f02cb230 [file] [log] [blame]
//
// ========================================================================
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.spdy.server.proxy;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.jetty.spdy.api.GoAwayResultInfo;
import org.eclipse.jetty.spdy.api.PingResultInfo;
import org.eclipse.jetty.spdy.api.RstInfo;
import org.eclipse.jetty.spdy.api.Session;
import org.eclipse.jetty.spdy.api.Stream;
import org.eclipse.jetty.spdy.api.StreamFrameListener;
import org.eclipse.jetty.spdy.api.StreamStatus;
import org.eclipse.jetty.spdy.api.SynInfo;
import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Fields;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/**
* <p>{@link ProxyEngineSelector} is the main entry point for push stream events of a jetty SPDY proxy. It receives the
* push stream frames from the clients, checks if there's an appropriate {@link ProxyServerInfo} for the given target
* host and forwards the push to a {@link ProxyEngine} for the protocol defined in {@link ProxyServerInfo}.</p>
*
* <p>If no {@link ProxyServerInfo} can be found for the given target host or no {@link ProxyEngine} can be found for
* the given protocol, it resets the client stream.</p>
*
* <p>This class also provides configuration for the proxy rules.</p>
*/
public class ProxyEngineSelector extends ServerSessionFrameListener.Adapter
{
protected final Logger LOG = Log.getLogger(getClass());
private final Map<String, ProxyServerInfo> proxyInfos = new ConcurrentHashMap<>();
private final Map<String, ProxyEngine> proxyEngines = new ConcurrentHashMap<>();
@Override
public final StreamFrameListener onSyn(final Stream clientStream, SynInfo clientSynInfo)
{
if (LOG.isDebugEnabled())
LOG.debug("C -> P {} on {}", clientSynInfo, clientStream);
final Session clientSession = clientStream.getSession();
short clientVersion = clientSession.getVersion();
Fields headers = new Fields(clientSynInfo.getHeaders(), false);
Fields.Field hostHeader = headers.get(HTTPSPDYHeader.HOST.name(clientVersion));
if (hostHeader == null)
{
if (LOG.isDebugEnabled())
LOG.debug("No host header found: " + headers);
rst(clientStream);
return null;
}
String host = hostHeader.getValue();
int colon = host.indexOf(':');
if (colon >= 0)
host = host.substring(0, colon);
ProxyServerInfo proxyServerInfo = getProxyServerInfo(host);
if (proxyServerInfo == null)
{
if (LOG.isDebugEnabled())
LOG.debug("No matching ProxyServerInfo found for: " + host);
rst(clientStream);
return null;
}
String protocol = proxyServerInfo.getProtocol();
ProxyEngine proxyEngine = proxyEngines.get(protocol);
if (proxyEngine == null)
{
if (LOG.isDebugEnabled())
LOG.debug("No matching ProxyEngine found for: " + protocol);
rst(clientStream);
return null;
}
if (LOG.isDebugEnabled())
LOG.debug("Forwarding request: {} -> {}", clientSynInfo, proxyServerInfo);
return proxyEngine.proxy(clientStream, clientSynInfo, proxyServerInfo);
}
@Override
public void onPing(Session clientSession, PingResultInfo pingResultInfo)
{
// We do not know to which upstream server
// to send the PING so we just ignore it
}
@Override
public void onGoAway(Session session, GoAwayResultInfo goAwayResultInfo)
{
// TODO:
}
public Map<String, ProxyEngine> getProxyEngines()
{
return new HashMap<>(proxyEngines);
}
public void setProxyEngines(Map<String, ProxyEngine> proxyEngines)
{
this.proxyEngines.clear();
this.proxyEngines.putAll(proxyEngines);
}
public ProxyEngine getProxyEngine(String protocol)
{
return proxyEngines.get(protocol);
}
public void putProxyEngine(String protocol, ProxyEngine proxyEngine)
{
proxyEngines.put(protocol, proxyEngine);
}
public Map<String, ProxyServerInfo> getProxyServerInfos()
{
return new HashMap<>(proxyInfos);
}
protected ProxyServerInfo getProxyServerInfo(String host)
{
return proxyInfos.get(host);
}
public void setProxyServerInfos(Map<String, ProxyServerInfo> proxyServerInfos)
{
this.proxyInfos.clear();
this.proxyInfos.putAll(proxyServerInfos);
}
public void putProxyServerInfo(String host, ProxyServerInfo proxyServerInfo)
{
proxyInfos.put(host, proxyServerInfo);
}
private void rst(Stream stream)
{
RstInfo rstInfo = new RstInfo(stream.getId(), StreamStatus.REFUSED_STREAM);
stream.getSession().rst(rstInfo, Callback.Adapter.INSTANCE);
}
public static class ProxyServerInfo
{
private final String protocol;
private final String host;
private final InetSocketAddress address;
public ProxyServerInfo(String protocol, String host, int port)
{
this.protocol = protocol;
this.host = host;
this.address = new InetSocketAddress(host, port);
}
public String getProtocol()
{
return protocol;
}
public String getHost()
{
return host;
}
public InetSocketAddress getAddress()
{
return address;
}
@Override
public String toString()
{
return "ProxyServerInfo{" +
"protocol='" + protocol + '\'' +
", host='" + host + '\'' +
", address=" + address +
'}';
}
}
}