// Copyright 2012 Georg-August-Universität Göttingen, Germany // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package de.ugoe.cs.autoquest.httpmonitor.proxy; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.util.Iterator; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.client.api.ContentProvider; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.proxy.ProxyServlet; import de.ugoe.cs.autoquest.httpmonitor.exchange.Status; import de.ugoe.cs.util.console.Console; /** *

* the servlet deployed in the web server of the proxy that proxies all incoming messages and * forwards a copy of recorded exchanges to the exchange listener manager. It is based on the * proxy servlet provided by jetty. It extends the servlet and implements all hooks required to * get access to the exchanged requests and responses. Each hook forwards the tracked data to the * exchange listener. On exchange completion, the exchange listener ensures a logging of the * recorded exchange. *

* * @author Patrick Harms */ class HttpMonitoringProxyServlet extends ProxyServlet { /** */ private static final long serialVersionUID = 1L; /** * the proxied server */ private String proxiedServer; /** * the port of the proxied server */ private int proxiedPort; /** * the exchange listener to handle the different events happening when an exchange is proxied */ private transient ExchangeListenerManager exchangeListenerManager; /** *

* initializes the servlet with the proxied server and the exchange listener *

* * @param proxiedServer the proxied server * @param proxiedPort the port of the proxied server * @param exchangeListenerManager the exchange listener to handle the different events * happening when an exchange is proxied */ HttpMonitoringProxyServlet(String proxiedServer, int proxiedPort, ExchangeListenerManager exchangeListenerManager) { this.proxiedServer = proxiedServer; this.proxiedPort = proxiedPort; this.exchangeListenerManager = exchangeListenerManager; } /* (non-Javadoc) * @see org.eclipse.jetty.proxy.ProxyServlet#rewriteURI(HttpServletRequest) */ @Override protected URI rewriteURI(HttpServletRequest request) { try { return new URI(request.getScheme(), null, proxiedServer, proxiedPort, request.getPathInfo(), request.getQueryString(), null); } catch (URISyntaxException e) { Console.printerrln("could not rewrite URI: " + e); Console.logException(e); return null; } } /* (non-Javadoc) * @see org.eclipse.jetty.proxy.ProxyServlet#customizeProxyRequest(Request, HttpServletRequest) */ @Override protected void customizeProxyRequest(Request proxyRequest, HttpServletRequest request) { super.customizeProxyRequest(proxyRequest, request); exchangeListenerManager.onRequest(request); proxyRequest.content(new DubbingContentProvider(request, proxyRequest.getContent())); } /* (non-Javadoc) * @see ProxyServlet#onResponseContent(HttpServletRequest, HttpServletResponse, Response, byte[], int, int) */ @Override protected void onResponseContent(HttpServletRequest request, HttpServletResponse response, Response proxyResponse, byte[] buffer, int offset, int length) throws IOException { super.onResponseContent(request, response, proxyResponse, buffer, offset, length); exchangeListenerManager.onResponseContent(request, ByteBuffer.wrap(buffer, offset, length)); } /* (non-Javadoc) * @see ProxyServlet#onResponseSuccess(HttpServletRequest, HttpServletResponse, Response) */ @Override protected void onResponseSuccess(HttpServletRequest request, HttpServletResponse response, Response proxyResponse) { exchangeListenerManager.onResponse(request, response); exchangeListenerManager.onFinish(request, Status.SUCCESS); super.onResponseSuccess(request, response, proxyResponse); } /* (non-Javadoc) * @see ProxyServlet#onResponseFailure(HttpServletRequest, HttpServletResponse, Response, Throwable) */ @Override protected void onResponseFailure(HttpServletRequest request, HttpServletResponse response, Response proxyResponse, Throwable failure) { exchangeListenerManager.onResponse(request, response); exchangeListenerManager.onFinish(request, Status.FAILURE); super.onResponseFailure(request, response, proxyResponse, failure); } /** * This content provided is required to copy the content of a proxied request. It uses * delegation to wrap the original content provided but to also copy the data and forward * it to the exchange listener manager. */ private class DubbingContentProvider implements ContentProvider { /** * the dubbed request */ private HttpServletRequest request; /** * the original content provider of which the data is copied */ private ContentProvider delegate; /** * initializes this content provider with the copied request and the delegate. */ public DubbingContentProvider(HttpServletRequest request, ContentProvider delegate) { this.request = request; this.delegate = delegate; } /* (non-Javadoc) * @see java.lang.Iterable#iterator() */ @Override public Iterator iterator() { return new DubbingByteBufferIterator(request, delegate.iterator()); } /* (non-Javadoc) * @see org.eclipse.jetty.client.api.ContentProvider#getLength() */ @Override public long getLength() { return delegate.getLength(); } } /** * This iterator is used to implement the {@link DubbingContentProvider}. It works in the * same manner and uses delegation for wrapping the original iterator and forwards all copied * data to the exchange listener manager. */ public class DubbingByteBufferIterator implements Iterator { /** * the dubbed request */ private HttpServletRequest request; /** * the original iterator of which the data is copied */ private Iterator delegate; /** * initializes this iterator with the copied request and the delegate. */ public DubbingByteBufferIterator(HttpServletRequest request, Iterator delegate) { this.request = request; this.delegate = delegate; } /* (non-Javadoc) * @see java.util.Iterator#hasNext() */ @Override public boolean hasNext() { return delegate.hasNext(); } /* (non-Javadoc) * @see java.util.Iterator#next() */ @Override public ByteBuffer next() { ByteBuffer next = delegate.next(); exchangeListenerManager.onRequestContent(request, next.duplicate()); return next; } /* (non-Javadoc) * @see java.util.Iterator#remove() */ @Override public void remove() { delegate.remove(); } } }