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.nio.ByteBuffer;
24  import java.util.concurrent.TimeUnit;
25  
26  import javax.servlet.AsyncContext;
27  import javax.servlet.ServletConfig;
28  import javax.servlet.ServletException;
29  import javax.servlet.http.HttpServletRequest;
30  import javax.servlet.http.HttpServletResponse;
31  
32  import org.eclipse.jetty.client.api.ContentProvider;
33  import org.eclipse.jetty.client.api.Request;
34  import org.eclipse.jetty.client.api.Response;
35  import org.eclipse.jetty.client.api.Result;
36  import org.eclipse.jetty.client.util.InputStreamContentProvider;
37  import org.eclipse.jetty.http.HttpVersion;
38  import org.eclipse.jetty.util.Callback;
39  
40  /**
41   * <p>Servlet 3.0 asynchronous proxy servlet.</p>
42   * <p>The request processing is asynchronous, but the I/O is blocking.</p>
43   *
44   * @see AsyncProxyServlet
45   * @see AsyncMiddleManServlet
46   * @see ConnectHandler
47   */
48  public class ProxyServlet extends AbstractProxyServlet
49  {
50      @Override
51      protected void service(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
52      {
53          final int requestId = getRequestId(request);
54  
55          String rewrittenTarget = rewriteTarget(request);
56  
57          if (_log.isDebugEnabled())
58          {
59              StringBuffer uri = request.getRequestURL();
60              if (request.getQueryString() != null)
61                  uri.append("?").append(request.getQueryString());
62              if (_log.isDebugEnabled())
63                  _log.debug("{} rewriting: {} -> {}", requestId, uri, rewrittenTarget);
64          }
65  
66          if (rewrittenTarget == null)
67          {
68              onProxyRewriteFailed(request, response);
69              return;
70          }
71  
72          final Request proxyRequest = getHttpClient().newRequest(rewrittenTarget)
73                  .method(request.getMethod())
74                  .version(HttpVersion.fromString(request.getProtocol()));
75  
76          copyRequestHeaders(request, proxyRequest);
77  
78          addProxyHeaders(request, proxyRequest);
79  
80          final AsyncContext asyncContext = request.startAsync();
81          // We do not timeout the continuation, but the proxy request
82          asyncContext.setTimeout(0);
83          proxyRequest.timeout(getTimeout(), TimeUnit.MILLISECONDS);
84  
85          if (hasContent(request))
86              proxyRequest.content(proxyRequestContent(request, response, proxyRequest));
87  
88          sendProxyRequest(request, response, proxyRequest);
89      }
90  
91      protected ContentProvider proxyRequestContent(HttpServletRequest request, HttpServletResponse response, Request proxyRequest) throws IOException
92      {
93          return new ProxyInputStreamContentProvider(request, response, proxyRequest, request.getInputStream());
94      }
95  
96      protected Response.Listener newProxyResponseListener(HttpServletRequest request, HttpServletResponse response)
97      {
98          return new ProxyResponseListener(request, response);
99      }
100 
101     protected void onResponseContent(HttpServletRequest request, HttpServletResponse response, Response proxyResponse, byte[] buffer, int offset, int length, Callback callback)
102     {
103         try
104         {
105             if (_log.isDebugEnabled())
106                 _log.debug("{} proxying content to downstream: {} bytes", getRequestId(request), length);
107             response.getOutputStream().write(buffer, offset, length);
108             callback.succeeded();
109         }
110         catch (Throwable x)
111         {
112             callback.failed(x);
113         }
114     }
115 
116     /**
117      * <p>Convenience extension of {@link ProxyServlet} that offers transparent proxy functionalities.</p>
118      *
119      * @see TransparentDelegate
120      */
121     public static class Transparent extends ProxyServlet
122     {
123         private final TransparentDelegate delegate = new TransparentDelegate(this);
124 
125         @Override
126         public void init(ServletConfig config) throws ServletException
127         {
128             super.init(config);
129             delegate.init(config);
130         }
131 
132         @Override
133         protected String rewriteTarget(HttpServletRequest request)
134         {
135             return delegate.rewriteTarget(request);
136         }
137     }
138 
139     protected class ProxyResponseListener extends Response.Listener.Adapter
140     {
141         private final HttpServletRequest request;
142         private final HttpServletResponse response;
143 
144         protected ProxyResponseListener(HttpServletRequest request, HttpServletResponse response)
145         {
146             this.request = request;
147             this.response = response;
148         }
149 
150         @Override
151         public void onBegin(Response proxyResponse)
152         {
153             response.setStatus(proxyResponse.getStatus());
154         }
155 
156         @Override
157         public void onHeaders(Response proxyResponse)
158         {
159             onServerResponseHeaders(request, response, proxyResponse);
160         }
161 
162         @Override
163         public void onContent(final Response proxyResponse, ByteBuffer content, final Callback callback)
164         {
165             byte[] buffer;
166             int offset;
167             int length = content.remaining();
168             if (content.hasArray())
169             {
170                 buffer = content.array();
171                 offset = content.arrayOffset();
172             }
173             else
174             {
175                 buffer = new byte[length];
176                 content.get(buffer);
177                 offset = 0;
178             }
179 
180             onResponseContent(request, response, proxyResponse, buffer, offset, length, new Callback()
181             {
182                 @Override
183                 public void succeeded()
184                 {
185                     callback.succeeded();
186                 }
187 
188                 @Override
189                 public void failed(Throwable x)
190                 {
191                     callback.failed(x);
192                     proxyResponse.abort(x);
193                 }
194             });
195         }
196 
197         @Override
198         public void onComplete(Result result)
199         {
200             if (result.isSucceeded())
201                 onProxyResponseSuccess(request, response, result.getResponse());
202             else
203                 onProxyResponseFailure(request, response, result.getResponse(), result.getFailure());
204             if (_log.isDebugEnabled())
205                 _log.debug("{} proxying complete", getRequestId(request));
206         }
207     }
208 
209     protected class ProxyInputStreamContentProvider extends InputStreamContentProvider
210     {
211         private final HttpServletResponse response;
212         private final Request proxyRequest;
213         private final HttpServletRequest request;
214 
215         protected ProxyInputStreamContentProvider(HttpServletRequest request, HttpServletResponse response, Request proxyRequest, InputStream input)
216         {
217             super(input);
218             this.request = request;
219             this.response = response;
220             this.proxyRequest = proxyRequest;
221         }
222 
223         @Override
224         public long getLength()
225         {
226             return request.getContentLength();
227         }
228 
229         @Override
230         protected ByteBuffer onRead(byte[] buffer, int offset, int length)
231         {
232             if (_log.isDebugEnabled())
233                 _log.debug("{} proxying content to upstream: {} bytes", getRequestId(request), length);
234             return onRequestContent(request, proxyRequest, buffer, offset, length);
235         }
236 
237         protected ByteBuffer onRequestContent(HttpServletRequest request, Request proxyRequest, byte[] buffer, int offset, int length)
238         {
239             return super.onRead(buffer, offset, length);
240         }
241 
242         @Override
243         protected void onReadFailure(Throwable failure)
244         {
245             onClientRequestFailure(request, proxyRequest, response, failure);
246         }
247     }
248 }