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.server.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.GoAwayResultInfo;
27  import org.eclipse.jetty.spdy.api.PingResultInfo;
28  import org.eclipse.jetty.spdy.api.RstInfo;
29  import org.eclipse.jetty.spdy.api.Session;
30  import org.eclipse.jetty.spdy.api.Stream;
31  import org.eclipse.jetty.spdy.api.StreamFrameListener;
32  import org.eclipse.jetty.spdy.api.StreamStatus;
33  import org.eclipse.jetty.spdy.api.SynInfo;
34  import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
35  import org.eclipse.jetty.spdy.server.http.HTTPSPDYHeader;
36  import org.eclipse.jetty.util.Callback;
37  import org.eclipse.jetty.util.Fields;
38  import org.eclipse.jetty.util.log.Log;
39  import org.eclipse.jetty.util.log.Logger;
40  
41  /**
42   * <p>{@link ProxyEngineSelector} is the main entry point for push stream events of a jetty SPDY proxy. It receives the
43   * push stream frames from the clients, checks if there's an appropriate {@link ProxyServerInfo} for the given target
44   * host and forwards the push to a {@link ProxyEngine} for the protocol defined in {@link ProxyServerInfo}.</p>
45   *
46   * <p>If no {@link ProxyServerInfo} can be found for the given target host or no {@link ProxyEngine} can be found for
47   * the given protocol, it resets the client stream.</p>
48   *
49   * <p>This class also provides configuration for the proxy rules.</p>
50   */
51  public class ProxyEngineSelector extends ServerSessionFrameListener.Adapter
52  {
53      protected final Logger LOG = Log.getLogger(getClass());
54      private final Map<String, ProxyServerInfo> proxyInfos = new ConcurrentHashMap<>();
55      private final Map<String, ProxyEngine> proxyEngines = new ConcurrentHashMap<>();
56  
57      @Override
58      public final StreamFrameListener onSyn(final Stream clientStream, SynInfo clientSynInfo)
59      {
60          LOG.debug("C -> P {} on {}", clientSynInfo, clientStream);
61  
62          final Session clientSession = clientStream.getSession();
63          short clientVersion = clientSession.getVersion();
64          Fields headers = new Fields(clientSynInfo.getHeaders(), false);
65  
66          Fields.Field hostHeader = headers.get(HTTPSPDYHeader.HOST.name(clientVersion));
67          if (hostHeader == null)
68          {
69              LOG.debug("No host header found: " + headers);
70              rst(clientStream);
71              return null;
72          }
73  
74          String host = hostHeader.value();
75          int colon = host.indexOf(':');
76          if (colon >= 0)
77              host = host.substring(0, colon);
78  
79          ProxyServerInfo proxyServerInfo = getProxyServerInfo(host);
80          if (proxyServerInfo == null)
81          {
82              LOG.debug("No matching ProxyServerInfo found for: " + host);
83              rst(clientStream);
84              return null;
85          }
86  
87          String protocol = proxyServerInfo.getProtocol();
88          ProxyEngine proxyEngine = proxyEngines.get(protocol);
89          if (proxyEngine == null)
90          {
91              LOG.debug("No matching ProxyEngine found for: " + protocol);
92              rst(clientStream);
93              return null;
94          }
95          LOG.debug("Forwarding request: {} -> {}", clientSynInfo, proxyServerInfo);
96          return proxyEngine.proxy(clientStream, clientSynInfo, proxyServerInfo);
97      }
98  
99      @Override
100     public void onPing(Session clientSession, PingResultInfo pingResultInfo)
101     {
102         // We do not know to which upstream server
103         // to send the PING so we just ignore it
104     }
105 
106     @Override
107     public void onGoAway(Session session, GoAwayResultInfo goAwayResultInfo)
108     {
109         // TODO:
110     }
111 
112     public Map<String, ProxyEngine> getProxyEngines()
113     {
114         return new HashMap<>(proxyEngines);
115     }
116 
117     public void setProxyEngines(Map<String, ProxyEngine> proxyEngines)
118     {
119         this.proxyEngines.clear();
120         this.proxyEngines.putAll(proxyEngines);
121     }
122 
123     public ProxyEngine getProxyEngine(String protocol)
124     {
125         return proxyEngines.get(protocol);
126     }
127 
128     public void putProxyEngine(String protocol, ProxyEngine proxyEngine)
129     {
130         proxyEngines.put(protocol, proxyEngine);
131     }
132 
133     public Map<String, ProxyServerInfo> getProxyServerInfos()
134     {
135         return new HashMap<>(proxyInfos);
136     }
137 
138     protected ProxyServerInfo getProxyServerInfo(String host)
139     {
140         return proxyInfos.get(host);
141     }
142 
143     public void setProxyServerInfos(Map<String, ProxyServerInfo> proxyServerInfos)
144     {
145         this.proxyInfos.clear();
146         this.proxyInfos.putAll(proxyServerInfos);
147     }
148 
149     public void putProxyServerInfo(String host, ProxyServerInfo proxyServerInfo)
150     {
151         proxyInfos.put(host, proxyServerInfo);
152     }
153 
154     private void rst(Stream stream)
155     {
156         RstInfo rstInfo = new RstInfo(stream.getId(), StreamStatus.REFUSED_STREAM);
157         stream.getSession().rst(rstInfo, new Callback.Adapter());
158     }
159 
160     public static class ProxyServerInfo
161     {
162         private final String protocol;
163         private final String host;
164         private final InetSocketAddress address;
165 
166         public ProxyServerInfo(String protocol, String host, int port)
167         {
168             this.protocol = protocol;
169             this.host = host;
170             this.address = new InetSocketAddress(host, port);
171         }
172 
173         public String getProtocol()
174         {
175             return protocol;
176         }
177 
178         public String getHost()
179         {
180             return host;
181         }
182 
183         public InetSocketAddress getAddress()
184         {
185             return address;
186         }
187 
188         @Override
189         public String toString()
190         {
191             return "ProxyServerInfo{" +
192                     "protocol='" + protocol + '\'' +
193                     ", host='" + host + '\'' +
194                     ", address=" + address +
195                     '}';
196         }
197     }
198 }