View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2015 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.proxy;
20  
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.net.URI;
24  import java.nio.ByteBuffer;
25  import java.util.concurrent.TimeUnit;
26  
27  import javax.servlet.AsyncContext;
28  import javax.servlet.ServletConfig;
29  import javax.servlet.ServletException;
30  import javax.servlet.http.HttpServletRequest;
31  import javax.servlet.http.HttpServletResponse;
32  
33  import org.eclipse.jetty.client.api.ContentProvider;
34  import org.eclipse.jetty.client.api.Request;
35  import org.eclipse.jetty.client.api.Response;
36  import org.eclipse.jetty.client.api.Result;
37  import org.eclipse.jetty.client.util.InputStreamContentProvider;
38  import org.eclipse.jetty.http.HttpVersion;
39  import org.eclipse.jetty.util.Callback;
40  
41  /**
42   * <p>Servlet 3.0 asynchronous proxy servlet.</p>
43   * <p>The request processing is asynchronous, but the I/O is blocking.</p>
44   *
45   * @see AsyncProxyServlet
46   * @see AsyncMiddleManServlet
47   * @see ConnectHandler
48   */
49  public class ProxyServlet extends AbstractProxyServlet
50  {
51      @Override
52      protected void service(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
53      {
54          final int requestId = getRequestId(request);
55  
56          URI rewrittenURI = rewriteURI(request);
57  
58          if (_log.isDebugEnabled())
59          {
60              StringBuffer uri = request.getRequestURL();
61              if (request.getQueryString() != null)
62                  uri.append("?").append(request.getQueryString());
63              if (_log.isDebugEnabled())
64                  _log.debug("{} rewriting: {} -> {}", requestId, uri, rewrittenURI);
65          }
66  
67          if (rewrittenURI == null)
68          {
69              onRewriteFailed(request, response);
70              return;
71          }
72  
73          final Request proxyRequest = getHttpClient().newRequest(rewrittenURI)
74                  .method(request.getMethod())
75                  .version(HttpVersion.fromString(request.getProtocol()));
76  
77          copyHeaders(request, proxyRequest);
78  
79          addProxyHeaders(request, proxyRequest);
80  
81          final AsyncContext asyncContext = request.startAsync();
82          // We do not timeout the continuation, but the proxy request
83          asyncContext.setTimeout(0);
84          proxyRequest.timeout(getTimeout(), TimeUnit.MILLISECONDS);
85  
86          if (hasContent(request))
87              proxyRequest.content(proxyRequestContent(proxyRequest, request));
88  
89          customizeProxyRequest(proxyRequest, request);
90  
91          sendProxyRequest(request, response, proxyRequest);
92      }
93  
94      /**
95       * @deprecated use {@link #copyRequestHeaders(HttpServletRequest, Request)} instead
96       */
97      @Deprecated
98      protected void copyHeaders(HttpServletRequest clientRequest, Request proxyRequest)
99      {
100         copyRequestHeaders(clientRequest, proxyRequest);
101     }
102 
103     protected ContentProvider proxyRequestContent(final Request proxyRequest, final HttpServletRequest request) throws IOException
104     {
105         return new ProxyInputStreamContentProvider(proxyRequest, request, request.getInputStream());
106     }
107 
108     protected Response.Listener newProxyResponseListener(HttpServletRequest request, HttpServletResponse response)
109     {
110         return new ProxyResponseListener(request, response);
111     }
112 
113     protected void onClientRequestFailure(Request proxyRequest, HttpServletRequest request, Throwable failure)
114     {
115         if (_log.isDebugEnabled())
116             _log.debug(getRequestId(request) + " client request failure", failure);
117         proxyRequest.abort(failure);
118     }
119 
120     /**
121      * @deprecated use {@link #onProxyRewriteFailed(HttpServletRequest, HttpServletResponse)}
122      */
123     @Deprecated
124     protected void onRewriteFailed(HttpServletRequest request, HttpServletResponse response) throws IOException
125     {
126         onProxyRewriteFailed(request, response);
127     }
128 
129     /**
130      * @deprecated use {@link #onServerResponseHeaders(HttpServletRequest, HttpServletResponse, Response)}
131      */
132     @Deprecated
133     protected void onResponseHeaders(HttpServletRequest request, HttpServletResponse response, Response proxyResponse)
134     {
135         onServerResponseHeaders(request, response, proxyResponse);
136     }
137 
138     // TODO: remove in Jetty 9.3, only here for backward compatibility.
139     @Override
140     protected String filterServerResponseHeader(HttpServletRequest clientRequest, Response serverResponse, String headerName, String headerValue)
141     {
142         return filterResponseHeader(clientRequest, headerName, headerValue);
143     }
144 
145     protected void onResponseContent(HttpServletRequest request, HttpServletResponse response, Response proxyResponse, byte[] buffer, int offset, int length, Callback callback)
146     {
147         try
148         {
149             if (_log.isDebugEnabled())
150                 _log.debug("{} proxying content to downstream: {} bytes", getRequestId(request), length);
151             response.getOutputStream().write(buffer, offset, length);
152             callback.succeeded();
153         }
154         catch (Throwable x)
155         {
156             callback.failed(x);
157         }
158     }
159 
160     /**
161      * @deprecated Use {@link #onProxyResponseSuccess(HttpServletRequest, HttpServletResponse, Response)}
162      */
163     @Deprecated
164     protected void onResponseSuccess(HttpServletRequest request, HttpServletResponse response, Response proxyResponse)
165     {
166         onProxyResponseSuccess(request, response, proxyResponse);
167     }
168 
169     /**
170      * @deprecated Use {@link #onProxyResponseFailure(HttpServletRequest, HttpServletResponse, Response, Throwable)}
171      */
172     @Deprecated
173     protected void onResponseFailure(HttpServletRequest request, HttpServletResponse response, Response proxyResponse, Throwable failure)
174     {
175         onProxyResponseFailure(request, response, proxyResponse, failure);
176     }
177 
178     /**
179      * @deprecated use {@link #rewriteTarget(HttpServletRequest)}
180      */
181     @Deprecated
182     protected URI rewriteURI(HttpServletRequest request)
183     {
184         String newTarget = rewriteTarget(request);
185         return newTarget == null ? null : URI.create(newTarget);
186     }
187 
188     /**
189      * @deprecated use {@link #sendProxyRequest(HttpServletRequest, HttpServletResponse, Request)}
190      */
191     @Deprecated
192     protected void customizeProxyRequest(Request proxyRequest, HttpServletRequest request)
193     {
194     }
195 
196     /**
197      * Extension point for remote server response header filtering.
198      * The default implementation returns the header value as is.
199      * If null is returned, this header won't be forwarded back to the client.
200      *
201      * @param headerName the header name
202      * @param headerValue the header value
203      * @param request the request to proxy
204      * @return filteredHeaderValue the new header value
205      * @deprecated use {@link #filterServerResponseHeader(HttpServletRequest, Response, String, String)} instead
206      */
207     @Deprecated
208     protected String filterResponseHeader(HttpServletRequest request, String headerName, String headerValue)
209     {
210         return headerValue;
211     }
212 
213     /**
214      * <p>Convenience extension of {@link ProxyServlet} that offers transparent proxy functionalities.</p>
215      *
216      * @see TransparentDelegate
217      */
218     public static class Transparent extends ProxyServlet
219     {
220         private final TransparentDelegate delegate = new TransparentDelegate(this);
221 
222         @Override
223         public void init(ServletConfig config) throws ServletException
224         {
225             super.init(config);
226             delegate.init(config);
227         }
228 
229         @Override
230         protected URI rewriteURI(HttpServletRequest request)
231         {
232             return URI.create(delegate.rewriteTarget(request));
233         }
234     }
235 
236     protected class ProxyResponseListener extends Response.Listener.Adapter
237     {
238         private final HttpServletRequest request;
239         private final HttpServletResponse response;
240 
241         protected ProxyResponseListener(HttpServletRequest request, HttpServletResponse response)
242         {
243             this.request = request;
244             this.response = response;
245         }
246 
247         @Override
248         public void onBegin(Response proxyResponse)
249         {
250             response.setStatus(proxyResponse.getStatus());
251         }
252 
253         @Override
254         public void onHeaders(Response proxyResponse)
255         {
256             onResponseHeaders(request, response, proxyResponse);
257         }
258 
259         @Override
260         public void onContent(final Response proxyResponse, ByteBuffer content, final Callback callback)
261         {
262             byte[] buffer;
263             int offset;
264             int length = content.remaining();
265             if (content.hasArray())
266             {
267                 buffer = content.array();
268                 offset = content.arrayOffset();
269             }
270             else
271             {
272                 buffer = new byte[length];
273                 content.get(buffer);
274                 offset = 0;
275             }
276 
277             onResponseContent(request, response, proxyResponse, buffer, offset, length, new Callback()
278             {
279                 @Override
280                 public void succeeded()
281                 {
282                     callback.succeeded();
283                 }
284 
285                 @Override
286                 public void failed(Throwable x)
287                 {
288                     callback.failed(x);
289                     proxyResponse.abort(x);
290                 }
291             });
292         }
293 
294         @Override
295         public void onComplete(Result result)
296         {
297             if (result.isSucceeded())
298                 onResponseSuccess(request, response, result.getResponse());
299             else
300                 onResponseFailure(request, response, result.getResponse(), result.getFailure());
301             if (_log.isDebugEnabled())
302                 _log.debug("{} proxying complete", getRequestId(request));
303         }
304     }
305 
306     protected class ProxyInputStreamContentProvider extends InputStreamContentProvider
307     {
308         private final Request proxyRequest;
309         private final HttpServletRequest request;
310 
311         protected ProxyInputStreamContentProvider(Request proxyRequest, HttpServletRequest request, InputStream input)
312         {
313             super(input);
314             this.proxyRequest = proxyRequest;
315             this.request = request;
316         }
317 
318         @Override
319         public long getLength()
320         {
321             return request.getContentLength();
322         }
323 
324         @Override
325         protected ByteBuffer onRead(byte[] buffer, int offset, int length)
326         {
327             if (_log.isDebugEnabled())
328                 _log.debug("{} proxying content to upstream: {} bytes", getRequestId(request), length);
329             return onRequestContent(proxyRequest, request, buffer, offset, length);
330         }
331 
332         protected ByteBuffer onRequestContent(Request proxyRequest, final HttpServletRequest request, byte[] buffer, int offset, int length)
333         {
334             return super.onRead(buffer, offset, length);
335         }
336 
337         @Override
338         protected void onReadFailure(Throwable failure)
339         {
340             onClientRequestFailure(proxyRequest, request, failure);
341         }
342     }
343 }