View Javadoc

1   // ========================================================================
2   // $Id: ProxyServlet.java 800 2006-08-20 00:01:46Z gregw $
3   // Copyright 2004-2004 Mort Bay Consulting Pty. Ltd.
4   // ------------------------------------------------------------------------
5   // Licensed under the Apache License, Version 2.0 (the "License");
6   // you may not use this file except in compliance with the License.
7   // You may obtain a copy of the License at 
8   // http://www.apache.org/licenses/LICENSE-2.0
9   // Unless required by applicable law or agreed to in writing, software
10  // distributed under the License is distributed on an "AS IS" BASIS,
11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  // See the License for the specific language governing permissions and
13  // limitations under the License.
14  // ========================================================================
15  
16  package org.mortbay.servlet;
17  
18  import java.io.IOException;
19  import java.io.InputStream;
20  import java.io.OutputStream;
21  import java.net.HttpURLConnection;
22  import java.net.InetSocketAddress;
23  import java.net.Socket;
24  import java.net.URL;
25  import java.net.URLConnection;
26  import java.util.Enumeration;
27  import java.util.HashSet;
28  
29  import javax.servlet.Servlet;
30  import javax.servlet.ServletConfig;
31  import javax.servlet.ServletContext;
32  import javax.servlet.ServletException;
33  import javax.servlet.ServletRequest;
34  import javax.servlet.ServletResponse;
35  import javax.servlet.http.HttpServletRequest;
36  import javax.servlet.http.HttpServletResponse;
37  
38  import org.mortbay.util.IO;
39  
40  
41  
42  /**
43   * EXPERIMENTAL Proxy servlet.
44   * @author gregw
45   *
46   */
47  public class ProxyServlet implements Servlet
48  {
49      private int _tunnelTimeoutMs=300000;
50      
51      protected HashSet _DontProxyHeaders = new HashSet();
52      {
53          _DontProxyHeaders.add("proxy-connection");
54          _DontProxyHeaders.add("connection");
55          _DontProxyHeaders.add("keep-alive");
56          _DontProxyHeaders.add("transfer-encoding");
57          _DontProxyHeaders.add("te");
58          _DontProxyHeaders.add("trailer");
59          _DontProxyHeaders.add("proxy-authorization");
60          _DontProxyHeaders.add("proxy-authenticate");
61          _DontProxyHeaders.add("upgrade");
62      }
63      
64      private ServletConfig config;
65      private ServletContext context;
66      
67      /* (non-Javadoc)
68       * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
69       */
70      public void init(ServletConfig config) throws ServletException
71      {
72          this.config=config;
73          this.context=config.getServletContext();
74      }
75  
76      /* (non-Javadoc)
77       * @see javax.servlet.Servlet#getServletConfig()
78       */
79      public ServletConfig getServletConfig()
80      {
81          return config;
82      }
83  
84      /* (non-Javadoc)
85       * @see javax.servlet.Servlet#service(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
86       */
87      public void service(ServletRequest req, ServletResponse res) throws ServletException,
88              IOException
89      {
90          HttpServletRequest request = (HttpServletRequest)req;
91          HttpServletResponse response = (HttpServletResponse)res;
92          if ("CONNECT".equalsIgnoreCase(request.getMethod()))
93          {
94              handleConnect(request,response);
95          }
96          else
97          {
98              String uri=request.getRequestURI();
99              if (request.getQueryString()!=null)
100                 uri+="?"+request.getQueryString();
101             URL url = new URL(request.getScheme(),
102                     		  request.getServerName(),
103                     		  request.getServerPort(),
104                     		  uri);
105             
106             context.log("URL="+url);
107 
108             URLConnection connection = url.openConnection();
109             connection.setAllowUserInteraction(false);
110             
111             // Set method
112             HttpURLConnection http = null;
113             if (connection instanceof HttpURLConnection)
114             {
115                 http = (HttpURLConnection)connection;
116                 http.setRequestMethod(request.getMethod());
117                 http.setInstanceFollowRedirects(false);
118             }
119 
120             // check connection header
121             String connectionHdr = request.getHeader("Connection");
122             if (connectionHdr!=null)
123             {
124                 connectionHdr=connectionHdr.toLowerCase();
125                 if (connectionHdr.equals("keep-alive")||
126                     connectionHdr.equals("close"))
127                     connectionHdr=null;
128             }
129             
130             // copy headers
131             boolean xForwardedFor=false;
132             boolean hasContent=false;
133             Enumeration enm = request.getHeaderNames();
134             while (enm.hasMoreElements())
135             {
136                 // TODO could be better than this!
137                 String hdr=(String)enm.nextElement();
138                 String lhdr=hdr.toLowerCase();
139 
140                 if (_DontProxyHeaders.contains(lhdr))
141                     continue;
142                 if (connectionHdr!=null && connectionHdr.indexOf(lhdr)>=0)
143                     continue;
144 
145                 if ("content-type".equals(lhdr))
146                     hasContent=true;
147 
148                 Enumeration vals = request.getHeaders(hdr);
149                 while (vals.hasMoreElements())
150                 {
151                     String val = (String)vals.nextElement();
152                     if (val!=null)
153                     {
154                         connection.addRequestProperty(hdr,val);
155                         context.log("req "+hdr+": "+val);
156                         xForwardedFor|="X-Forwarded-For".equalsIgnoreCase(hdr);
157                     }
158                 }
159             }
160 
161             // Proxy headers
162             connection.setRequestProperty("Via","1.1 (jetty)");
163             if (!xForwardedFor)
164                 connection.addRequestProperty("X-Forwarded-For",
165                                               request.getRemoteAddr());
166 
167             // a little bit of cache control
168             String cache_control = request.getHeader("Cache-Control");
169             if (cache_control!=null &&
170                 (cache_control.indexOf("no-cache")>=0 ||
171                  cache_control.indexOf("no-store")>=0))
172                 connection.setUseCaches(false);
173 
174             // customize Connection
175             
176             try
177             {    
178                 connection.setDoInput(true);
179                 
180                 // do input thang!
181                 InputStream in=request.getInputStream();
182                 if (hasContent)
183                 {
184                     connection.setDoOutput(true);
185                     IO.copy(in,connection.getOutputStream());
186                 }
187                 
188                 // Connect                
189                 connection.connect();    
190             }
191             catch (Exception e)
192             {
193                 context.log("proxy",e);
194             }
195             
196             InputStream proxy_in = null;
197 
198             // handler status codes etc.
199             int code=500;
200             if (http!=null)
201             {
202                 proxy_in = http.getErrorStream();
203                 
204                 code=http.getResponseCode();
205                 response.setStatus(code, http.getResponseMessage());
206                 context.log("response = "+http.getResponseCode());
207             }
208             
209             if (proxy_in==null)
210             {
211                 try {proxy_in=connection.getInputStream();}
212                 catch (Exception e)
213                 {
214                     context.log("stream",e);
215                     proxy_in = http.getErrorStream();
216                 }
217             }
218             
219             // clear response defaults.
220             response.setHeader("Date",null);
221             response.setHeader("Server",null);
222             
223             // set response headers
224             int h=0;
225             String hdr=connection.getHeaderFieldKey(h);
226             String val=connection.getHeaderField(h);
227             while(hdr!=null || val!=null)
228             {
229                 String lhdr = hdr!=null?hdr.toLowerCase():null;
230                 if (hdr!=null && val!=null && !_DontProxyHeaders.contains(lhdr))
231                     response.addHeader(hdr,val);
232 
233                 context.log("res "+hdr+": "+val);
234                 
235                 h++;
236                 hdr=connection.getHeaderFieldKey(h);
237                 val=connection.getHeaderField(h);
238             }
239             response.addHeader("Via","1.1 (jetty)");
240 
241             // Handle
242             if (proxy_in!=null)
243                 IO.copy(proxy_in,response.getOutputStream());
244             
245         }
246     }
247 
248 
249     /* ------------------------------------------------------------ */
250     public void handleConnect(HttpServletRequest request,
251                               HttpServletResponse response)
252         throws IOException
253     {
254         String uri = request.getRequestURI();
255         
256         context.log("CONNECT: "+uri);
257         
258         String port = "";
259         String host = "";
260         
261         int c = uri.indexOf(':');
262         if (c>=0)
263         {
264             port = uri.substring(c+1);
265             host = uri.substring(0,c);
266             if (host.indexOf('/')>0)
267                 host = host.substring(host.indexOf('/')+1);
268         }
269 
270         
271        
272 
273         InetSocketAddress inetAddress = new InetSocketAddress (host, Integer.parseInt(port));
274         
275         //if (isForbidden(HttpMessage.__SSL_SCHEME,addrPort.getHost(),addrPort.getPort(),false))
276         //{
277         //    sendForbid(request,response,uri);
278         //}
279         //else
280         {
281             InputStream in=request.getInputStream();
282             OutputStream out=response.getOutputStream();
283             
284             Socket socket = new Socket(inetAddress.getAddress(),inetAddress.getPort());
285             context.log("Socket: "+socket);
286             
287             response.setStatus(200);
288             response.setHeader("Connection","close");
289             response.flushBuffer();
290             
291             
292 
293             context.log("out<-in");
294             IO.copyThread(socket.getInputStream(),out);
295             context.log("in->out");
296             IO.copy(in,socket.getOutputStream());
297         }
298     }
299     
300     
301     
302     
303     /* (non-Javadoc)
304      * @see javax.servlet.Servlet#getServletInfo()
305      */
306     public String getServletInfo()
307     {
308         return "Proxy Servlet";
309     }
310 
311     /* (non-Javadoc)
312      * @see javax.servlet.Servlet#destroy()
313      */
314     public void destroy()
315     {
316 
317     }
318 }