View Javadoc

1   // ========================================================================
2   // $Id: ProxyServlet.java 3863 2008-10-21 04:35:26Z 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      
50      protected HashSet _DontProxyHeaders = new HashSet();
51      {
52          _DontProxyHeaders.add("proxy-connection");
53          _DontProxyHeaders.add("connection");
54          _DontProxyHeaders.add("keep-alive");
55          _DontProxyHeaders.add("transfer-encoding");
56          _DontProxyHeaders.add("te");
57          _DontProxyHeaders.add("trailer");
58          _DontProxyHeaders.add("proxy-authorization");
59          _DontProxyHeaders.add("proxy-authenticate");
60          _DontProxyHeaders.add("upgrade");
61      }
62      
63      private ServletConfig config;
64      private ServletContext context;
65      
66      /* (non-Javadoc)
67       * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
68       */
69      public void init(ServletConfig config) throws ServletException
70      {
71          this.config=config;
72          this.context=config.getServletContext();
73      }
74  
75      /* (non-Javadoc)
76       * @see javax.servlet.Servlet#getServletConfig()
77       */
78      public ServletConfig getServletConfig()
79      {
80          return config;
81      }
82  
83      /* (non-Javadoc)
84       * @see javax.servlet.Servlet#service(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
85       */
86      public void service(ServletRequest req, ServletResponse res) throws ServletException,
87              IOException
88      {
89          HttpServletRequest request = (HttpServletRequest)req;
90          HttpServletResponse response = (HttpServletResponse)res;
91          if ("CONNECT".equalsIgnoreCase(request.getMethod()))
92          {
93              handleConnect(request,response);
94          }
95          else
96          {
97              String uri=request.getRequestURI();
98              if (request.getQueryString()!=null)
99                  uri+="?"+request.getQueryString();
100             URL url = new URL(request.getScheme(),
101                     		  request.getServerName(),
102                     		  request.getServerPort(),
103                     		  uri);
104             
105             context.log("URL="+url);
106 
107             URLConnection connection = url.openConnection();
108             connection.setAllowUserInteraction(false);
109             
110             // Set method
111             HttpURLConnection http = null;
112             if (connection instanceof HttpURLConnection)
113             {
114                 http = (HttpURLConnection)connection;
115                 http.setRequestMethod(request.getMethod());
116                 http.setInstanceFollowRedirects(false);
117             }
118 
119             // check connection header
120             String connectionHdr = request.getHeader("Connection");
121             if (connectionHdr!=null)
122             {
123                 connectionHdr=connectionHdr.toLowerCase();
124                 if (connectionHdr.equals("keep-alive")||
125                     connectionHdr.equals("close"))
126                     connectionHdr=null;
127             }
128             
129             // copy headers
130             boolean xForwardedFor=false;
131             boolean hasContent=false;
132             Enumeration enm = request.getHeaderNames();
133             while (enm.hasMoreElements())
134             {
135                 // TODO could be better than this!
136                 String hdr=(String)enm.nextElement();
137                 String lhdr=hdr.toLowerCase();
138 
139                 if (_DontProxyHeaders.contains(lhdr))
140                     continue;
141                 if (connectionHdr!=null && connectionHdr.indexOf(lhdr)>=0)
142                     continue;
143 
144                 if ("content-type".equals(lhdr))
145                     hasContent=true;
146 
147                 Enumeration vals = request.getHeaders(hdr);
148                 while (vals.hasMoreElements())
149                 {
150                     String val = (String)vals.nextElement();
151                     if (val!=null)
152                     {
153                         connection.addRequestProperty(hdr,val);
154                         context.log("req "+hdr+": "+val);
155                         xForwardedFor|="X-Forwarded-For".equalsIgnoreCase(hdr);
156                     }
157                 }
158             }
159 
160             // Proxy headers
161             connection.setRequestProperty("Via","1.1 (jetty)");
162             if (!xForwardedFor)
163                 connection.addRequestProperty("X-Forwarded-For",
164                                               request.getRemoteAddr());
165 
166             // a little bit of cache control
167             String cache_control = request.getHeader("Cache-Control");
168             if (cache_control!=null &&
169                 (cache_control.indexOf("no-cache")>=0 ||
170                  cache_control.indexOf("no-store")>=0))
171                 connection.setUseCaches(false);
172 
173             // customize Connection
174             
175             try
176             {    
177                 connection.setDoInput(true);
178                 
179                 // do input thang!
180                 InputStream in=request.getInputStream();
181                 if (hasContent)
182                 {
183                     connection.setDoOutput(true);
184                     IO.copy(in,connection.getOutputStream());
185                 }
186                 
187                 // Connect                
188                 connection.connect();    
189             }
190             catch (Exception e)
191             {
192                 context.log("proxy",e);
193             }
194             
195             InputStream proxy_in = null;
196 
197             // handler status codes etc.
198             int code=500;
199             if (http!=null)
200             {
201                 proxy_in = http.getErrorStream();
202                 
203                 code=http.getResponseCode();
204                 response.setStatus(code, http.getResponseMessage());
205                 context.log("response = "+http.getResponseCode());
206             }
207             
208             if (proxy_in==null)
209             {
210                 try {proxy_in=connection.getInputStream();}
211                 catch (Exception e)
212                 {
213                     context.log("stream",e);
214                     proxy_in = http.getErrorStream();
215                 }
216             }
217             
218             // clear response defaults.
219             response.setHeader("Date",null);
220             response.setHeader("Server",null);
221             
222             // set response headers
223             int h=0;
224             String hdr=connection.getHeaderFieldKey(h);
225             String val=connection.getHeaderField(h);
226             while(hdr!=null || val!=null)
227             {
228                 String lhdr = hdr!=null?hdr.toLowerCase():null;
229                 if (hdr!=null && val!=null && !_DontProxyHeaders.contains(lhdr))
230                     response.addHeader(hdr,val);
231 
232                 context.log("res "+hdr+": "+val);
233                 
234                 h++;
235                 hdr=connection.getHeaderFieldKey(h);
236                 val=connection.getHeaderField(h);
237             }
238             response.addHeader("Via","1.1 (jetty)");
239 
240             // Handle
241             if (proxy_in!=null)
242                 IO.copy(proxy_in,response.getOutputStream());
243             
244         }
245     }
246 
247 
248     /* ------------------------------------------------------------ */
249     public void handleConnect(HttpServletRequest request,
250                               HttpServletResponse response)
251         throws IOException
252     {
253         String uri = request.getRequestURI();
254         
255         context.log("CONNECT: "+uri);
256         
257         String port = "";
258         String host = "";
259         
260         int c = uri.indexOf(':');
261         if (c>=0)
262         {
263             port = uri.substring(c+1);
264             host = uri.substring(0,c);
265             if (host.indexOf('/')>0)
266                 host = host.substring(host.indexOf('/')+1);
267         }
268 
269         
270        
271 
272         InetSocketAddress inetAddress = new InetSocketAddress (host, Integer.parseInt(port));
273         
274         //if (isForbidden(HttpMessage.__SSL_SCHEME,addrPort.getHost(),addrPort.getPort(),false))
275         //{
276         //    sendForbid(request,response,uri);
277         //}
278         //else
279         {
280             InputStream in=request.getInputStream();
281             OutputStream out=response.getOutputStream();
282             
283             Socket socket = new Socket(inetAddress.getAddress(),inetAddress.getPort());
284             context.log("Socket: "+socket);
285             
286             response.setStatus(200);
287             response.setHeader("Connection","close");
288             response.flushBuffer();
289             
290             
291 
292             context.log("out<-in");
293             IO.copyThread(socket.getInputStream(),out);
294             context.log("in->out");
295             IO.copy(in,socket.getOutputStream());
296         }
297     }
298     
299     
300     
301     
302     /* (non-Javadoc)
303      * @see javax.servlet.Servlet#getServletInfo()
304      */
305     public String getServletInfo()
306     {
307         return "Proxy Servlet";
308     }
309 
310     /* (non-Javadoc)
311      * @see javax.servlet.Servlet#destroy()
312      */
313     public void destroy()
314     {
315 
316     }
317 }