source: trunk/autoquest-httpmonitor/src/main/java/de/ugoe/cs/autoquest/httpmonitor/proxy/ExchangeListener.java @ 1991

Last change on this file since 1991 was 1991, checked in by pharms, 9 years ago
  • added ordering id for requests and responses
File size: 13.6 KB
RevLine 
[1374]1//   Copyright 2012 Georg-August-Universität Göttingen, Germany
2//
3//   Licensed under the Apache License, Version 2.0 (the "License");
4//   you may not use this file except in compliance with the License.
5//   You may obtain a copy of the License at
6//
7//       http://www.apache.org/licenses/LICENSE-2.0
8//
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
15package de.ugoe.cs.autoquest.httpmonitor.proxy;
16
17import java.math.BigInteger;
18import java.nio.ByteBuffer;
19import java.util.Collection;
20import java.util.Enumeration;
21import java.util.LinkedList;
22import java.util.List;
23import java.util.logging.Level;
24
25import javax.servlet.http.HttpServletRequest;
26import javax.servlet.http.HttpServletResponse;
27
28import de.ugoe.cs.autoquest.httpmonitor.HttpMonitorExchangeHandler;
[1991]29import de.ugoe.cs.autoquest.httpmonitor.IdGenerator;
[1561]30import de.ugoe.cs.autoquest.plugin.http.logdata.Address;
31import de.ugoe.cs.autoquest.plugin.http.logdata.Content;
32import de.ugoe.cs.autoquest.plugin.http.logdata.Cookie;
33import de.ugoe.cs.autoquest.plugin.http.logdata.Cookies;
34import de.ugoe.cs.autoquest.plugin.http.logdata.Header;
35import de.ugoe.cs.autoquest.plugin.http.logdata.Headers;
36import de.ugoe.cs.autoquest.plugin.http.logdata.HttpExchange;
37import de.ugoe.cs.autoquest.plugin.http.logdata.HttpRequest;
38import de.ugoe.cs.autoquest.plugin.http.logdata.HttpResponse;
39import de.ugoe.cs.autoquest.plugin.http.logdata.Method;
40import de.ugoe.cs.autoquest.plugin.http.logdata.ObjectFactory;
41import de.ugoe.cs.autoquest.plugin.http.logdata.Protocol;
42import de.ugoe.cs.autoquest.plugin.http.logdata.Status;
[1374]43import de.ugoe.cs.util.console.Console;
44
45/**
46 * <p>
[1382]47 * recording an exchange can not be done in one step. This is due to the fact, that the proxy
48 * servlet notifies different processing states for requests and response. An exchange listener
[1991]49 * records all these events. On the occurrence of the final event, it compiles an
50 * {@link HttpExchange} and forwards it to the exchange handler. When receiving the first event
51 * of the request, it also determines a respective ordering id. The same applies for the response.
52 * The ordering ids are retrieved from a provided id generator.
[1374]53 * </p>
54 *
55 * @author Patrick Harms
56 */
57class ExchangeListener {
58   
59    /**
[1382]60     * <p>
61     * the exchange handler to forward compiles exchanges to
62     * </p>
[1374]63     */
64    private HttpMonitorExchangeHandler exchangeHandler;
65   
66    /**
[1382]67     * <p>
[1991]68     * the id generator used to generate ordering ids for requests and responses
69     * </p>
70     */
71    private IdGenerator idGenerator;
72   
73    /**
74     * <p>
[1382]75     * the request of compiled exchange
76     * </p>
[1374]77     */
78    private HttpServletRequest request;
[1991]79   
80    /**
81     * <p>
82     * the ordering id for the request
83     * </p>
84     */
85    private long requestOrderingId;
[1374]86
87    /**
[1382]88     * <p>
89     * the content of the request of compiled exchange
90     * </p>
[1374]91     */
92    private List<ByteBuffer> requestData = new LinkedList<ByteBuffer>();
93   
94    /**
[1382]95     * <p>
96     * the response of compiled exchange
97     * </p>
[1374]98     */
99    private HttpServletResponse response;
[1991]100   
101    /**
102     * <p>
103     * the ordering id for the response
104     * </p>
105     */
106    private long responseOrderingId;
[1374]107
108    /**
[1382]109     * <p>
110     * the content of the response of compiled exchange
111     * </p>
[1374]112     */
113    private List<ByteBuffer> responseData = new LinkedList<ByteBuffer>();
114   
115    /**
[1382]116     * <p>
117     * the last time an event for the exchange was received (used for supporting timeouts)
118     * </p>
[1374]119     */
120    private long lastUpdate = System.currentTimeMillis();
121   
122    /**
[1382]123     * <p>
124     * initialized the exchange listener with the exchange handler to forward compiled exchanges to
[1991]125     * and the id generator to be used for generating ordering ids for requests and responses
[1382]126     * </p>
127     *
128     * @param exchangeHandler the exchange handler to forward compiled exchanges to
[1991]129     * @param idGenerator     the id generator to used for generating ordering ids
[1374]130     */
[1991]131    ExchangeListener(HttpMonitorExchangeHandler exchangeHandler, IdGenerator idGenerator) {
[1374]132        this.exchangeHandler = exchangeHandler;
[1991]133        this.idGenerator = idGenerator;
[1374]134    }
135
136    /**
[1382]137     * <p>
138     * called, when the request was received by the proxy
139     * </p>
[1374]140     *
[1382]141     * @param request the request of the exchange
[1374]142     */
143    public void onRequest(HttpServletRequest request) throws IllegalStateException {
144        Console.traceln(Level.FINEST, this + ": onRequest " + request);
145
146        if (request == null) {
147            throw new IllegalArgumentException("request must not be null");
148        }
149
150        lastUpdate = System.currentTimeMillis();
151        this.request = request;
[1991]152        this.requestOrderingId = idGenerator.getNextId();
[1374]153    }
154
155    /**
[1382]156     * <p>
157     * called, when some content of the request was processed by the proxy
158     * </p>
[1374]159     *
[1382]160     * @param data the processed content of the request of the exchange
[1374]161     */
162    public void onRequestContent(ByteBuffer data) {
163        Console.traceln(Level.FINEST, this + ": onRequestContent " + data);
164
165        if (data == null) {
166            throw new IllegalArgumentException("data must not be null");
167        }
168
169        lastUpdate = System.currentTimeMillis();
170        requestData.add(data);
171    }
172   
173    /**
[1382]174     * <p>
175     * called, when the response is to be returned by the proxy
176     * </p>
[1374]177     *
[1382]178     * @param response the response of the exchange
[1374]179     */
180    public void onResponse(HttpServletResponse response) {
181        Console.traceln(Level.FINEST, this + ": onResponse " + response);
182
183        if (response == null) {
184            throw new IllegalArgumentException("response must not be null");
185        }
186
187        lastUpdate = System.currentTimeMillis();
188        this.response = response;
[1991]189        this.responseOrderingId = idGenerator.getNextId();
[1374]190    }
191   
192    /**
[1382]193     * <p>
194     * called, when some content of the response was processed by the proxy
195     * </p>
[1374]196     *
[1382]197     * @param data the processed content of the response of the exchange
[1374]198     */
199    public void onResponseContent(ByteBuffer data) {
200        Console.traceln(Level.FINEST, this + ": onResponseContent " + data);
201
202        if (data == null) {
203            throw new IllegalArgumentException("data must not be null");
204        }
205
206        lastUpdate = System.currentTimeMillis();
207        responseData.add(data);
208    }
209   
210    /**
[1382]211     * <p>
212     * called, when proxy finished proxying a request
213     * </p>
[1374]214     *
[1382]215     * @param status the status of the proxying after finalization
[1374]216     */
217    public void onFinish(Status status) {
218        Console.traceln(Level.FINEST, this + ": onFinish " + status);
219
220        if (status == null) {
221            throw new IllegalArgumentException("status must not be null");
222        }
223
224        lastUpdate = System.currentTimeMillis();
225        sendToExchangeHandler(status);
226    }
227   
228    /**
[1382]229     * @return the request of the exchange
[1374]230     */
231    HttpServletRequest getRequest() {
232        return request;
233    }
234
235    /**
[1382]236     * @return the last time this listener received an event
[1374]237     */
238    long getLastUpdate() {
239        return lastUpdate;
240    }
241
242    /**
[1382]243     * <p>
244     * convenience method to compile an {@link HttpExchange} and send it to the exchange handler
245     * after finalization of the exchange.
246     * </p>
247     *
248     * @param status the status of the proxying after finalization
[1374]249     */
250    private void sendToExchangeHandler(Status status) {
251        ObjectFactory eventObjectFactory = new ObjectFactory();
252        HttpExchange exchange = eventObjectFactory.createHttpExchange();
253       
254        exchange.setStatus(status);
255       
256        // the remote address
257        Address address = eventObjectFactory.createAddress();
258        address.setIp(request.getRemoteAddr());
259        address.setHost(request.getRemoteHost());
260        address.setPort(BigInteger.valueOf(request.getRemotePort()));
261        exchange.setSender(address);
262       
263        // the local address
264        address = eventObjectFactory.createAddress();
265        address.setIp(request.getLocalAddr());
266        address.setHost(request.getLocalName());
267        address.setPort(BigInteger.valueOf(request.getLocalPort()));
268        exchange.setReceiver(address);
269       
[1991]270        exchange.setRequest(map(request, requestOrderingId, eventObjectFactory));
271        exchange.setResponse(map(response, responseOrderingId, eventObjectFactory));
[1374]272       
273        exchangeHandler.handleHttpExchange(exchange);
274    }
275
276    /**
[1382]277     * <p>
278     * convenience method to map an {@link HttpServletRequest} to an {@link HttpRequest}
279     * </p>
[1374]280     */
[1991]281    private HttpRequest map(HttpServletRequest request,
282                            long               requestOrderingId,
283                            ObjectFactory      eventObjectFactory)
284    {
[1374]285        HttpRequest eventRequest = eventObjectFactory.createHttpRequest();
[1991]286        eventRequest.setOrderingId(requestOrderingId);
[1374]287        eventRequest.setMethod(Method.fromValue(request.getMethod()));
288        eventRequest.setProtocol(Protocol.fromValue(request.getProtocol()));
289        eventRequest.setUrl(request.getRequestURL().toString());
290        eventRequest.setQuery(request.getQueryString());
291       
292        Headers headers = eventObjectFactory.createHeaders();
293        Enumeration<String> headerNames = request.getHeaderNames();
294        while (headerNames.hasMoreElements()) {
295            String headerName = headerNames.nextElement();
296           
297            Enumeration<String> headerValues = request.getHeaders(headerName);
298            while (headerValues.hasMoreElements()) {
299                Header header = eventObjectFactory.createHeader();
300                header.setKey(headerName);
301                header.setValue(headerValues.nextElement());
302                headers.getHeader().add(header);
303            }
304        }
305        eventRequest.setHeaders(headers);
306       
307        if (request.getCookies() != null) {
308            Cookies cookies = eventObjectFactory.createCookies();
309            for (javax.servlet.http.Cookie requestCookie : request.getCookies()) {
310                Cookie cookie = eventObjectFactory.createCookie();
311                cookie.setComment(requestCookie.getComment());
312                cookie.setDomain(requestCookie.getDomain());
313                cookie.setIsHttpOnly(requestCookie.isHttpOnly());
314                cookie.setIsSecure(requestCookie.getSecure());
315                cookie.setMaxAge(BigInteger.valueOf(requestCookie.getMaxAge()));
316                cookie.setName(requestCookie.getName());
317                cookie.setPath(requestCookie.getPath());
318                cookie.setValue(requestCookie.getValue());
319                cookie.setVersion(BigInteger.valueOf(requestCookie.getVersion()));
320                cookies.getCookie().add(cookie);
321            }
322            eventRequest.setCookies(cookies);
323        }
324       
325        eventRequest.setAuthType(request.getAuthType());
326        eventRequest.setRemoteUser(request.getRemoteUser());
327        eventRequest.setRequestedSessionId(request.getRequestedSessionId());
328       
329        if (requestData.size() > 0) {
330            Content content = eventObjectFactory.createContent();
331            content.setEncoding(request.getCharacterEncoding());
332            content.setType(request.getContentType());
333            content.setLength(request.getContentLength());
334            content.setData(createString(requestData));
335            eventRequest.setContent(content);
336        }
337       
338        return eventRequest;
339    }
340
341    /**
[1382]342     * <p>
343     * convenience method to map an {@link HttpServletResponse} to an {@link HttpResponse}
344     * </p>
[1374]345     */
[1991]346    private HttpResponse map(HttpServletResponse response,
347                             long                responseOrderingId,
348                             ObjectFactory       eventObjectFactory)
349    {
[1374]350        HttpResponse eventResponse = eventObjectFactory.createHttpResponse();
351       
352        eventResponse.setStatus(BigInteger.valueOf(response.getStatus()));
[1991]353        eventResponse.setOrderingId(responseOrderingId);
[1374]354
355        Headers headers = eventObjectFactory.createHeaders();
356        Collection<String> headerNames = response.getHeaderNames();
357        for (String headerName : headerNames) {
358            Collection<String> headerValues = response.getHeaders(headerName);
359            for (String headerValue : headerValues) {
360                Header header = eventObjectFactory.createHeader();
361                header.setKey(headerName);
362                header.setValue(headerValue);
363                headers.getHeader().add(header);
364            }
365        }
366        eventResponse.setHeaders(headers);
367       
368        if (responseData.size() > 0) {
369            Content content = eventObjectFactory.createContent();
370            content.setEncoding(response.getCharacterEncoding());
371            content.setType(response.getContentType());
372
373            String data = createString(responseData);
374            content.setLength(data.length());
375            content.setData(data);
376           
377            eventResponse.setContent(content);
378        }
379       
380        return eventResponse;
381    }
382
383    /**
[1382]384     * <p>
385     * convenience method to create a string out of recorded request or response content
386     * </p>
[1374]387     */
388    private String createString(List<ByteBuffer> bufferList) {
389        StringBuffer str = new StringBuffer();
390       
391        for (ByteBuffer buffer : bufferList) {
392            while (buffer.hasRemaining()) {
393                str.append((char) buffer.get());
394            }
395        }
396       
397        return str.toString();
398    }
399
400}
Note: See TracBrowser for help on using the repository browser.