View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2014 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.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          if (LOG.isDebugEnabled())
61              LOG.debug("C -> P {} on {}", clientSynInfo, clientStream);
62  
63          final Session clientSession = clientStream.getSession();
64          short clientVersion = clientSession.getVersion();
65          Fields headers = new Fields(clientSynInfo.getHeaders(), false);
66  
67          Fields.Field hostHeader = headers.get(HTTPSPDYHeader.HOST.name(clientVersion));
68          if (hostHeader == null)
69          {
70              if (LOG.isDebugEnabled())
71                  LOG.debug("No host header found: " + headers);
72              rst(clientStream);
73              return null;
74          }
75  
76          String host = hostHeader.getValue();
77          int colon = host.indexOf(':');
78          if (colon >= 0)
79              host = host.substring(0, colon);
80  
81          ProxyServerInfo proxyServerInfo = getProxyServerInfo(host);
82          if (proxyServerInfo == null)
83          {
84              if (LOG.isDebugEnabled())
85                  LOG.debug("No matching ProxyServerInfo found for: " + host);
86              rst(clientStream);
87              return null;
88          }
89  
90          String protocol = proxyServerInfo.getProtocol();
91          ProxyEngine proxyEngine = proxyEngines.get(protocol);
92          if (proxyEngine == null)
93          {
94              if (LOG.isDebugEnabled())
95                  LOG.debug("No matching ProxyEngine found for: " + protocol);
96              rst(clientStream);
97              return null;
98          }
99          if (LOG.isDebugEnabled())
100             LOG.debug("Forwarding request: {} -> {}", clientSynInfo, proxyServerInfo);
101         return proxyEngine.proxy(clientStream, clientSynInfo, proxyServerInfo);
102     }
103 
104     @Override
105     public void onPing(Session clientSession, PingResultInfo pingResultInfo)
106     {
107         // We do not know to which upstream server
108         // to send the PING so we just ignore it
109     }
110 
111     @Override
112     public void onGoAway(Session session, GoAwayResultInfo goAwayResultInfo)
113     {
114         // TODO:
115     }
116 
117     public Map<String, ProxyEngine> getProxyEngines()
118     {
119         return new HashMap<>(proxyEngines);
120     }
121 
122     public void setProxyEngines(Map<String, ProxyEngine> proxyEngines)
123     {
124         this.proxyEngines.clear();
125         this.proxyEngines.putAll(proxyEngines);
126     }
127 
128     public ProxyEngine getProxyEngine(String protocol)
129     {
130         return proxyEngines.get(protocol);
131     }
132 
133     public void putProxyEngine(String protocol, ProxyEngine proxyEngine)
134     {
135         proxyEngines.put(protocol, proxyEngine);
136     }
137 
138     public Map<String, ProxyServerInfo> getProxyServerInfos()
139     {
140         return new HashMap<>(proxyInfos);
141     }
142 
143     protected ProxyServerInfo getProxyServerInfo(String host)
144     {
145         return proxyInfos.get(host);
146     }
147 
148     public void setProxyServerInfos(Map<String, ProxyServerInfo> proxyServerInfos)
149     {
150         this.proxyInfos.clear();
151         this.proxyInfos.putAll(proxyServerInfos);
152     }
153 
154     public void putProxyServerInfo(String host, ProxyServerInfo proxyServerInfo)
155     {
156         proxyInfos.put(host, proxyServerInfo);
157     }
158 
159     private void rst(Stream stream)
160     {
161         RstInfo rstInfo = new RstInfo(stream.getId(), StreamStatus.REFUSED_STREAM);
162         stream.getSession().rst(rstInfo, Callback.Adapter.INSTANCE);
163     }
164 
165     public static class ProxyServerInfo
166     {
167         private final String protocol;
168         private final String host;
169         private final InetSocketAddress address;
170 
171         public ProxyServerInfo(String protocol, String host, int port)
172         {
173             this.protocol = protocol;
174             this.host = host;
175             this.address = new InetSocketAddress(host, port);
176         }
177 
178         public String getProtocol()
179         {
180             return protocol;
181         }
182 
183         public String getHost()
184         {
185             return host;
186         }
187 
188         public InetSocketAddress getAddress()
189         {
190             return address;
191         }
192 
193         @Override
194         public String toString()
195         {
196             return "ProxyServerInfo{" +
197                     "protocol='" + protocol + '\'' +
198                     ", host='" + host + '\'' +
199                     ", address=" + address +
200                     '}';
201         }
202     }
203 }