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.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      @Override
97      protected Response.Listener newProxyResponseListener(HttpServletRequest request, HttpServletResponse response)
98      {
99          return new ProxyResponseListener(request, response);
100     }
101 
102     protected void onResponseContent(HttpServletRequest request, HttpServletResponse response, Response proxyResponse, byte[] buffer, int offset, int length, Callback callback)
103     {
104         try
105         {
106             if (_log.isDebugEnabled())
107                 _log.debug("{} proxying content to downstream: {} bytes", getRequestId(request), length);
108             response.getOutputStream().write(buffer, offset, length);
109             callback.succeeded();
110         }
111         catch (Throwable x)
112         {
113             callback.failed(x);
114         }
115     }
116 
117     /**
118      * <p>Convenience extension of {@link ProxyServlet} that offers transparent proxy functionalities.</p>
119      *
120      * @see org.eclipse.jetty.proxy.AbstractProxyServlet.TransparentDelegate
121      */
122     public static class Transparent extends ProxyServlet
123     {
124         private final TransparentDelegate delegate = new TransparentDelegate(this);
125 
126         @Override
127         public void init(ServletConfig config) throws ServletException
128         {
129             super.init(config);
130             delegate.init(config);
131         }
132 
133         @Override
134         protected String rewriteTarget(HttpServletRequest request)
135         {
136             return delegate.rewriteTarget(request);
137         }
138     }
139 
140     protected class ProxyResponseListener extends Response.Listener.Adapter
141     {
142         private final HttpServletRequest request;
143         private final HttpServletResponse response;
144 
145         protected ProxyResponseListener(HttpServletRequest request, HttpServletResponse response)
146         {
147             this.request = request;
148             this.response = response;
149         }
150 
151         @Override
152         public void onBegin(Response proxyResponse)
153         {
154             response.setStatus(proxyResponse.getStatus());
155         }
156 
157         @Override
158         public void onHeaders(Response proxyResponse)
159         {
160             onServerResponseHeaders(request, response, proxyResponse);
161         }
162 
163         @Override
164         public void onContent(final Response proxyResponse, ByteBuffer content, final Callback callback)
165         {
166             byte[] buffer;
167             int offset;
168             int length = content.remaining();
169             if (content.hasArray())
170             {
171                 buffer = content.array();
172                 offset = content.arrayOffset();
173             }
174             else
175             {
176                 buffer = new byte[length];
177                 content.get(buffer);
178                 offset = 0;
179             }
180 
181             onResponseContent(request, response, proxyResponse, buffer, offset, length, new Callback()
182             {
183                 @Override
184                 public void succeeded()
185                 {
186                     callback.succeeded();
187                 }
188 
189                 @Override
190                 public void failed(Throwable x)
191                 {
192                     callback.failed(x);
193                     proxyResponse.abort(x);
194                 }
195             });
196         }
197 
198         @Override
199         public void onComplete(Result result)
200         {
201             if (result.isSucceeded())
202                 onProxyResponseSuccess(request, response, result.getResponse());
203             else
204                 onProxyResponseFailure(request, response, result.getResponse(), result.getFailure());
205             if (_log.isDebugEnabled())
206                 _log.debug("{} proxying complete", getRequestId(request));
207         }
208     }
209 
210     protected class ProxyInputStreamContentProvider extends InputStreamContentProvider
211     {
212         private final HttpServletResponse response;
213         private final Request proxyRequest;
214         private final HttpServletRequest request;
215 
216         protected ProxyInputStreamContentProvider(HttpServletRequest request, HttpServletResponse response, Request proxyRequest, InputStream input)
217         {
218             super(input);
219             this.request = request;
220             this.response = response;
221             this.proxyRequest = proxyRequest;
222         }
223 
224         @Override
225         public long getLength()
226         {
227             return request.getContentLength();
228         }
229 
230         @Override
231         protected ByteBuffer onRead(byte[] buffer, int offset, int length)
232         {
233             if (_log.isDebugEnabled())
234                 _log.debug("{} proxying content to upstream: {} bytes", getRequestId(request), length);
235             return onRequestContent(request, proxyRequest, buffer, offset, length);
236         }
237 
238         protected ByteBuffer onRequestContent(HttpServletRequest request, Request proxyRequest, byte[] buffer, int offset, int length)
239         {
240             return super.onRead(buffer, offset, length);
241         }
242 
243         @Override
244         protected void onReadFailure(Throwable failure)
245         {
246             onClientRequestFailure(request, proxyRequest, response, failure);
247         }
248     }
249 }