View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 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.server;
20  
21  import java.io.IOException;
22  import java.net.InetSocketAddress;
23  import java.nio.ByteBuffer;
24  import java.nio.channels.ReadPendingException;
25  import java.nio.channels.WritePendingException;
26  import java.util.Iterator;
27  
28  import org.eclipse.jetty.io.AbstractConnection;
29  import org.eclipse.jetty.io.Connection;
30  import org.eclipse.jetty.io.EndPoint;
31  import org.eclipse.jetty.util.BufferUtil;
32  import org.eclipse.jetty.util.Callback;
33  import org.eclipse.jetty.util.log.Log;
34  import org.eclipse.jetty.util.log.Logger;
35  
36  
37  /* ------------------------------------------------------------ */
38  /**
39   * ConnectionFactory for the PROXY Protocol.
40   * <p>This factory can be placed in front of any other connection factory
41   * to process the proxy line before the normal protocol handling</p>
42   *
43   * @see <a href="http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt">http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt</a>
44   */
45  public class ProxyConnectionFactory extends AbstractConnectionFactory
46  {
47      private static final Logger LOG = Log.getLogger(ProxyConnectionFactory.class);
48      private final String _next;
49  
50      /* ------------------------------------------------------------ */
51      /** Proxy Connection Factory that uses the next ConnectionFactory
52       * on the connector as the next protocol
53       */
54      public ProxyConnectionFactory()
55      {
56          super("proxy");
57          _next=null;
58      }
59  
60      public ProxyConnectionFactory(String nextProtocol)
61      {
62          super("proxy");
63          _next=nextProtocol;
64      }
65  
66      @Override
67      public Connection newConnection(Connector connector, EndPoint endp)
68      {
69          String next=_next;
70          if (next==null)
71          {
72              for (Iterator<String> i = connector.getProtocols().iterator();i.hasNext();)
73              {
74                  String p=i.next();
75                  if (getProtocol().equalsIgnoreCase(p))
76                  {
77                      next=i.next();
78                      break;
79                  }
80              }
81          }
82  
83          return new ProxyConnection(endp,connector,next);
84      }
85  
86      public static class ProxyConnection extends AbstractConnection
87      {
88          // 0     1 2       3       4 5 6
89          // 98765432109876543210987654321
90          // PROXY P R.R.R.R L.L.L.L R Lrn
91  
92          private final int[] __size = {29,23,21,13,5,3,1};
93          private final Connector _connector;
94          private final String _next;
95          private final StringBuilder _builder=new StringBuilder();
96          private final String[] _field=new String[6];
97          private int _fields;
98          private int _length;
99  
100         protected ProxyConnection(EndPoint endp, Connector connector, String next)
101         {
102             super(endp,connector.getExecutor());
103             _connector=connector;
104             _next=next;
105         }
106 
107         @Override
108         public void onOpen()
109         {
110             super.onOpen();
111             fillInterested();
112         }
113 
114         @Override
115         public void onFillable()
116         {
117             try
118             {
119                 ByteBuffer buffer=null;
120                 loop: while(true)
121                 {
122                     // Create a buffer that will not read too much data
123                     int size=Math.max(1,__size[_fields]-_builder.length());
124                     if (buffer==null || buffer.capacity()!=size)
125                         buffer=BufferUtil.allocate(size);
126                     else
127                         BufferUtil.clear(buffer);
128 
129                     // Read data
130                     int fill=getEndPoint().fill(buffer);
131                     if (fill<0)
132                     {
133                         getEndPoint().shutdownOutput();
134                         return;
135                     }
136                     if (fill==0)
137                     {
138                         fillInterested();
139                         return;
140                     }
141 
142                     _length+=fill;
143                     if (_length>=108)
144                     {
145                         LOG.warn("PROXY line too long {} for {}",_length,getEndPoint());
146                         close();
147                         return;
148                     }
149 
150                     // parse fields
151                     while (buffer.hasRemaining())
152                     {
153                         byte b = buffer.get();
154                         if (_fields<6)
155                         {
156                             if (b==' ' || b=='\r' && _fields==5)
157                             {
158                                 _field[_fields++]=_builder.toString();
159                                 _builder.setLength(0);
160                             }
161                             else if (b<' ')
162                             {
163                                 LOG.warn("Bad character {} for {}",b&0xFF,getEndPoint());
164                                 close();
165                                 return;
166                             }
167                             else
168                             {
169                                 _builder.append((char)b);
170                             }
171                         }
172                         else
173                         {
174                             if (b=='\n')
175                                 break loop;
176 
177                             LOG.warn("Bad CRLF for {}",getEndPoint());
178                             close();
179                             return;
180                         }
181                     }
182                 }
183 
184                 // Check proxy
185                 if (!"PROXY".equals(_field[0]))
186                 {
187                     LOG.warn("Not PROXY protocol for {}",getEndPoint());
188                     close();
189                     return;
190                 }
191 
192                 // Extract Addresses
193                 InetSocketAddress remote=new InetSocketAddress(_field[2],Integer.parseInt(_field[4]));
194                 InetSocketAddress local =new InetSocketAddress(_field[3],Integer.parseInt(_field[5]));
195 
196                 // Create the next protocol
197                 ConnectionFactory connectionFactory = _connector.getConnectionFactory(_next);
198                 if (connectionFactory == null)
199                 {
200                     LOG.info("Next protocol '{}' for {}",_next,getEndPoint());
201                     close();
202                     return;
203                 }
204 
205                 EndPoint endPoint = new ProxyEndPoint(getEndPoint(),remote,local);
206                 Connection newConnection = connectionFactory.newConnection(_connector, endPoint);
207                 endPoint.upgrade(newConnection);
208             }
209             catch (Throwable x)
210             {
211                 LOG.warn("PROXY error for "+getEndPoint(),x);
212                 close();
213             }
214         }
215     }
216 
217     public static class ProxyEndPoint implements EndPoint
218     {
219         private final EndPoint _endp;
220         private final InetSocketAddress _remote;
221         private final InetSocketAddress _local;
222 
223         public ProxyEndPoint(EndPoint endp, InetSocketAddress remote, InetSocketAddress local)
224         {
225             _endp=endp;
226             _remote=remote;
227             _local=local;
228         }
229 
230         @Override
231         public boolean isOptimizedForDirectBuffers()
232         {
233             return _endp.isOptimizedForDirectBuffers();
234         }
235 
236         public InetSocketAddress getLocalAddress()
237         {
238             return _local;
239         }
240 
241         public InetSocketAddress getRemoteAddress()
242         {
243             return _remote;
244         }
245 
246         public boolean isOpen()
247         {
248             return _endp.isOpen();
249         }
250 
251         public long getCreatedTimeStamp()
252         {
253             return _endp.getCreatedTimeStamp();
254         }
255 
256         public void shutdownOutput()
257         {
258             _endp.shutdownOutput();
259         }
260 
261         public boolean isOutputShutdown()
262         {
263             return _endp.isOutputShutdown();
264         }
265 
266         public boolean isInputShutdown()
267         {
268             return _endp.isInputShutdown();
269         }
270 
271         public void close()
272         {
273             _endp.close();
274         }
275 
276         public int fill(ByteBuffer buffer) throws IOException
277         {
278             return _endp.fill(buffer);
279         }
280 
281         public boolean flush(ByteBuffer... buffer) throws IOException
282         {
283             return _endp.flush(buffer);
284         }
285 
286         public Object getTransport()
287         {
288             return _endp.getTransport();
289         }
290 
291         public long getIdleTimeout()
292         {
293             return _endp.getIdleTimeout();
294         }
295 
296         public void setIdleTimeout(long idleTimeout)
297         {
298             _endp.setIdleTimeout(idleTimeout);
299         }
300 
301         public void fillInterested(Callback callback) throws ReadPendingException
302         {
303             _endp.fillInterested(callback);
304         }
305 
306         @Override
307         public boolean isFillInterested()
308         {
309             return _endp.isFillInterested();
310         }
311 
312         public void write(Callback callback, ByteBuffer... buffers) throws WritePendingException
313         {
314             _endp.write(callback,buffers);
315         }
316 
317         public Connection getConnection()
318         {
319             return _endp.getConnection();
320         }
321 
322         public void setConnection(Connection connection)
323         {
324             _endp.setConnection(connection);
325         }
326 
327         public void onOpen()
328         {
329             _endp.onOpen();
330         }
331 
332         public void onClose()
333         {
334             _endp.onClose();
335         }
336 
337         @Override
338         public void upgrade(Connection newConnection)
339         {
340             _endp.upgrade(newConnection);
341         }
342     }
343 }