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

Last change on this file was 1991, checked in by pharms, 9 years ago
  • added ordering id for requests and responses
File size: 13.6 KB
Line 
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;
29import de.ugoe.cs.autoquest.httpmonitor.IdGenerator;
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;
43import de.ugoe.cs.util.console.Console;
44
45/**
46 * <p>
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
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.
53 * </p>
54 *
55 * @author Patrick Harms
56 */
57class ExchangeListener {
58   
59    /**
60     * <p>
61     * the exchange handler to forward compiles exchanges to
62     * </p>
63     */
64    private HttpMonitorExchangeHandler exchangeHandler;
65   
66    /**
67     * <p>
68     * the id generator used to generate ordering ids for requests and responses
69     * </p>
70     */
71    private IdGenerator idGenerator;
72   
73    /**
74     * <p>
75     * the request of compiled exchange
76     * </p>
77     */
78    private HttpServletRequest request;
79   
80    /**
81     * <p>
82     * the ordering id for the request
83     * </p>
84     */
85    private long requestOrderingId;
86
87    /**
88     * <p>
89     * the content of the request of compiled exchange
90     * </p>
91     */
92    private List<ByteBuffer> requestData = new LinkedList<ByteBuffer>();
93   
94    /**
95     * <p>
96     * the response of compiled exchange
97     * </p>
98     */
99    private HttpServletResponse response;
100   
101    /**
102     * <p>
103     * the ordering id for the response
104     * </p>
105     */
106    private long responseOrderingId;
107
108    /**
109     * <p>
110     * the content of the response of compiled exchange
111     * </p>
112     */
113    private List<ByteBuffer> responseData = new LinkedList<ByteBuffer>();
114   
115    /**
116     * <p>
117     * the last time an event for the exchange was received (used for supporting timeouts)
118     * </p>
119     */
120    private long lastUpdate = System.currentTimeMillis();
121   
122    /**
123     * <p>
124     * initialized the exchange listener with the exchange handler to forward compiled exchanges to
125     * and the id generator to be used for generating ordering ids for requests and responses
126     * </p>
127     *
128     * @param exchangeHandler the exchange handler to forward compiled exchanges to
129     * @param idGenerator     the id generator to used for generating ordering ids
130     */
131    ExchangeListener(HttpMonitorExchangeHandler exchangeHandler, IdGenerator idGenerator) {
132        this.exchangeHandler = exchangeHandler;
133        this.idGenerator = idGenerator;
134    }
135
136    /**
137     * <p>
138     * called, when the request was received by the proxy
139     * </p>
140     *
141     * @param request the request of the exchange
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;
152        this.requestOrderingId = idGenerator.getNextId();
153    }
154
155    /**
156     * <p>
157     * called, when some content of the request was processed by the proxy
158     * </p>
159     *
160     * @param data the processed content of the request of the exchange
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    /**
174     * <p>
175     * called, when the response is to be returned by the proxy
176     * </p>
177     *
178     * @param response the response of the exchange
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;
189        this.responseOrderingId = idGenerator.getNextId();
190    }
191   
192    /**
193     * <p>
194     * called, when some content of the response was processed by the proxy
195     * </p>
196     *
197     * @param data the processed content of the response of the exchange
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    /**
211     * <p>
212     * called, when proxy finished proxying a request
213     * </p>
214     *
215     * @param status the status of the proxying after finalization
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    /**
229     * @return the request of the exchange
230     */
231    HttpServletRequest getRequest() {
232        return request;
233    }
234
235    /**
236     * @return the last time this listener received an event
237     */
238    long getLastUpdate() {
239        return lastUpdate;
240    }
241
242    /**
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
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       
270        exchange.setRequest(map(request, requestOrderingId, eventObjectFactory));
271        exchange.setResponse(map(response, responseOrderingId, eventObjectFactory));
272       
273        exchangeHandler.handleHttpExchange(exchange);
274    }
275
276    /**
277     * <p>
278     * convenience method to map an {@link HttpServletRequest} to an {@link HttpRequest}
279     * </p>
280     */
281    private HttpRequest map(HttpServletRequest request,
282                            long               requestOrderingId,
283                            ObjectFactory      eventObjectFactory)
284    {
285        HttpRequest eventRequest = eventObjectFactory.createHttpRequest();
286        eventRequest.setOrderingId(requestOrderingId);
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    /**
342     * <p>
343     * convenience method to map an {@link HttpServletResponse} to an {@link HttpResponse}
344     * </p>
345     */
346    private HttpResponse map(HttpServletResponse response,
347                             long                responseOrderingId,
348                             ObjectFactory       eventObjectFactory)
349    {
350        HttpResponse eventResponse = eventObjectFactory.createHttpResponse();
351       
352        eventResponse.setStatus(BigInteger.valueOf(response.getStatus()));
353        eventResponse.setOrderingId(responseOrderingId);
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    /**
384     * <p>
385     * convenience method to create a string out of recorded request or response content
386     * </p>
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.