View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.spdy.proxy;
20  
21  import java.net.InetSocketAddress;
22  import java.util.HashMap;
23  import java.util.Map;
24  import java.util.concurrent.ConcurrentHashMap;
25  
26  import org.eclipse.jetty.spdy.api.GoAwayInfo;
27  import org.eclipse.jetty.spdy.api.Headers;
28  import org.eclipse.jetty.spdy.api.PingInfo;
29  import org.eclipse.jetty.spdy.api.RstInfo;
30  import org.eclipse.jetty.spdy.api.Session;
31  import org.eclipse.jetty.spdy.api.Stream;
32  import org.eclipse.jetty.spdy.api.StreamFrameListener;
33  import org.eclipse.jetty.spdy.api.StreamStatus;
34  import org.eclipse.jetty.spdy.api.SynInfo;
35  import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
36  import org.eclipse.jetty.spdy.http.HTTPSPDYHeader;
37  import org.eclipse.jetty.util.log.Log;
38  import org.eclipse.jetty.util.log.Logger;
39  
40  /**
41   * <p>{@link ProxyEngineSelector} is the main entry point for syn stream events of a jetty SPDY proxy. It receives the
42   * syn stream frames from the clients, checks if there's an appropriate {@link ProxyServerInfo} for the given target
43   * host and forwards the syn to a {@link ProxyEngine} for the protocol defined in {@link ProxyServerInfo}.</p>
44   *
45   * <p>If no {@link ProxyServerInfo} can be found for the given target host or no {@link ProxyEngine} can be found for
46   * the given protocol, it resets the client stream.</p>
47   *
48   * <p>This class also provides configuration for the proxy rules.</p>
49   */
50  public class ProxyEngineSelector extends ServerSessionFrameListener.Adapter
51  {
52      protected final Logger logger = Log.getLogger(getClass());
53      private final Map<String, ProxyServerInfo> proxyInfos = new ConcurrentHashMap<>();
54      private final Map<String, ProxyEngine> proxyEngines = new ConcurrentHashMap<>();
55  
56      @Override
57      public final StreamFrameListener onSyn(final Stream clientStream, SynInfo clientSynInfo)
58      {
59          logger.debug("C -> P {} on {}", clientSynInfo, clientStream);
60  
61          final Session clientSession = clientStream.getSession();
62          short clientVersion = clientSession.getVersion();
63          Headers headers = new Headers(clientSynInfo.getHeaders(), false);
64  
65          Headers.Header hostHeader = headers.get(HTTPSPDYHeader.HOST.name(clientVersion));
66          if (hostHeader == null)
67          {
68              logger.debug("No host header found: " + headers);
69              rst(clientStream);
70              return null;
71          }
72  
73          String host = hostHeader.value();
74          int colon = host.indexOf(':');
75          if (colon >= 0)
76              host = host.substring(0, colon);
77  
78          ProxyServerInfo proxyServerInfo = getProxyServerInfo(host);
79          if (proxyServerInfo == null)
80          {
81              logger.debug("No matching ProxyServerInfo found for: " + host);
82              rst(clientStream);
83              return null;
84          }
85  
86          String protocol = proxyServerInfo.getProtocol();
87          ProxyEngine proxyEngine = proxyEngines.get(protocol);
88          if (proxyEngine == null)
89          {
90              logger.debug("No matching ProxyEngine found for: " + protocol);
91              rst(clientStream);
92              return null;
93          }
94  
95          return proxyEngine.proxy(clientStream, clientSynInfo, proxyServerInfo);
96      }
97  
98      @Override
99      public void onPing(Session clientSession, PingInfo pingInfo)
100     {
101         // We do not know to which upstream server
102         // to send the PING so we just ignore it
103     }
104 
105     @Override
106     public void onGoAway(Session session, GoAwayInfo goAwayInfo)
107     {
108         // TODO:
109     }
110 
111     public Map<String, ProxyEngine> getProxyEngines()
112     {
113         return new HashMap<>(proxyEngines);
114     }
115 
116     public void setProxyEngines(Map<String, ProxyEngine> proxyEngines)
117     {
118         this.proxyEngines.clear();
119         this.proxyEngines.putAll(proxyEngines);
120     }
121 
122     public ProxyEngine getProxyEngine(String protocol)
123     {
124         return proxyEngines.get(protocol);
125     }
126 
127     public void putProxyEngine(String protocol, ProxyEngine proxyEngine)
128     {
129         proxyEngines.put(protocol, proxyEngine);
130     }
131 
132     public Map<String, ProxyServerInfo> getProxyServerInfos()
133     {
134         return new HashMap<>(proxyInfos);
135     }
136 
137     protected ProxyServerInfo getProxyServerInfo(String host)
138     {
139         return proxyInfos.get(host);
140     }
141 
142     public void setProxyServerInfos(Map<String, ProxyServerInfo> proxyServerInfos)
143     {
144         this.proxyInfos.clear();
145         this.proxyInfos.putAll(proxyServerInfos);
146     }
147 
148     public void putProxyServerInfo(String host, ProxyServerInfo proxyServerInfo)
149     {
150         proxyInfos.put(host, proxyServerInfo);
151     }
152 
153     private void rst(Stream stream)
154     {
155         RstInfo rstInfo = new RstInfo(stream.getId(), StreamStatus.REFUSED_STREAM);
156         stream.getSession().rst(rstInfo);
157     }
158 
159     public static class ProxyServerInfo
160     {
161         private final String protocol;
162         private final String host;
163         private final InetSocketAddress address;
164 
165         public ProxyServerInfo(String protocol, String host, int port)
166         {
167             this.protocol = protocol;
168             this.host = host;
169             this.address = new InetSocketAddress(host, port);
170         }
171 
172         public String getProtocol()
173         {
174             return protocol;
175         }
176 
177         public String getHost()
178         {
179             return host;
180         }
181 
182         public InetSocketAddress getAddress()
183         {
184             return address;
185         }
186     }
187 }