Enhancements for the aai-common library
[aai/aai-common.git] / aai-els-onap-logging / src / main / java / org / onap / logging / filter / base / PayloadLoggingServletFilter.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP - Logging
4  * ================================================================================
5  * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Modifications Copyright (C) 2018 IBM.
8  * ================================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  * 
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  * 
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * ============LICENSE_END=========================================================
21  */
22
23 package org.onap.logging.filter.base;
24
25 import java.io.BufferedReader;
26 import java.io.ByteArrayInputStream;
27 import java.io.ByteArrayOutputStream;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.InputStreamReader;
31 import java.io.PrintWriter;
32 import java.util.zip.GZIPInputStream;
33 import javax.servlet.Filter;
34 import javax.servlet.FilterChain;
35 import javax.servlet.FilterConfig;
36 import javax.servlet.ReadListener;
37 import javax.servlet.ServletException;
38 import javax.servlet.ServletInputStream;
39 import javax.servlet.ServletOutputStream;
40 import javax.servlet.ServletRequest;
41 import javax.servlet.ServletResponse;
42 import javax.servlet.WriteListener;
43 import javax.servlet.http.HttpServletRequest;
44 import javax.servlet.http.HttpServletRequestWrapper;
45 import javax.servlet.http.HttpServletResponse;
46 import javax.servlet.http.HttpServletResponseWrapper;
47
48 public class PayloadLoggingServletFilter extends AbstractServletFilter implements Filter {
49
50     private static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(PayloadLoggingServletFilter.class);
51
52     private static class ByteArrayServletStream extends ServletOutputStream {
53         ByteArrayOutputStream baos;
54
55         ByteArrayServletStream(ByteArrayOutputStream baos) {
56             this.baos = baos;
57         }
58
59         @Override
60         public void write(int param) throws IOException {
61             baos.write(param);
62         }
63
64         @Override
65         public boolean isReady() {
66             return true;
67         }
68
69         @Override
70         public void setWriteListener(WriteListener arg0) {
71             // this method does nothing
72         }
73     }
74
75     private static class ByteArrayPrintWriter extends PrintWriter {
76         private ByteArrayOutputStream baos;
77         private int errorCode = -1;
78         private String errorMsg = "";
79         private boolean errored = false;
80
81         public ByteArrayPrintWriter(ByteArrayOutputStream out) {
82             super(out);
83             this.baos = out;
84         }
85
86         public ServletOutputStream getStream() {
87             return new ByteArrayServletStream(baos);
88         }
89
90         public Boolean hasErrored() {
91             return errored;
92         }
93
94         public int getErrorCode() {
95             return errorCode;
96         }
97
98         public String getErrorMsg() {
99             return errorMsg;
100         }
101
102         public void setError(int code) {
103             errorCode = code;
104             errored = true;
105         }
106
107         public void setError(int code, String msg) {
108             errorMsg = msg;
109             errorCode = code;
110             errored = true;
111         }
112
113     }
114
115     private class BufferedServletInputStream extends ServletInputStream {
116         ByteArrayInputStream bais;
117
118         public BufferedServletInputStream(ByteArrayInputStream bais) {
119             this.bais = bais;
120         }
121
122         @Override
123         public int available() {
124             return bais.available();
125         }
126
127         @Override
128         public int read() {
129             return bais.read();
130         }
131
132         @Override
133         public int read(byte[] buf, int off, int len) {
134             return bais.read(buf, off, len);
135         }
136
137         @Override
138         public boolean isFinished() {
139             return available() < 1;
140         }
141
142         @Override
143         public boolean isReady() {
144             return true;
145         }
146
147         @Override
148         public void setReadListener(ReadListener arg0) {
149             // this method does nothing
150         }
151
152     }
153
154     private class BufferedRequestWrapper extends HttpServletRequestWrapper {
155         ByteArrayInputStream bais;
156         ByteArrayOutputStream baos;
157         BufferedServletInputStream bsis;
158         byte[] buffer;
159
160         public BufferedRequestWrapper(HttpServletRequest req) throws IOException {
161             super(req);
162
163             InputStream is = req.getInputStream();
164             baos = new ByteArrayOutputStream();
165             byte[] buf = new byte[1024];
166             int letti;
167             while ((letti = is.read(buf)) > 0) {
168                 baos.write(buf, 0, letti);
169             }
170             buffer = baos.toByteArray();
171         }
172
173         @Override
174         public ServletInputStream getInputStream() {
175             try {
176                 bais = new ByteArrayInputStream(buffer);
177                 bsis = new BufferedServletInputStream(bais);
178             } catch (Exception ex) {
179                 log.error("Exception in getInputStream", ex);
180             }
181             return bsis;
182         }
183
184         public byte[] getBuffer() {
185             return buffer;
186         }
187     }
188
189     @Override
190     public void init(FilterConfig filterConfig) throws ServletException {
191         // this method does nothing
192     }
193
194     @Override
195     public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
196             throws IOException, ServletException {
197         final HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
198         BufferedRequestWrapper bufferedRequest = new BufferedRequestWrapper(httpRequest);
199
200         StringBuilder requestHeaders = new StringBuilder("REQUEST|");
201         requestHeaders.append(httpRequest.getMethod());
202         requestHeaders.append(":");
203         requestHeaders.append(httpRequest.getRequestURL().toString());
204         requestHeaders.append("|");
205         requestHeaders.append(getSecureRequestHeaders(httpRequest));
206         log.info(requestHeaders.toString());
207
208         log.info("REQUEST BODY|" + new String(bufferedRequest.getBuffer()));
209
210         final HttpServletResponse response = (HttpServletResponse) servletResponse;
211         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
212         final ByteArrayPrintWriter pw = new ByteArrayPrintWriter(baos);
213
214         HttpServletResponse wrappedResp = new HttpServletResponseWrapper(response) {
215             @Override
216             public PrintWriter getWriter() {
217                 return pw;
218             }
219
220             @Override
221             public ServletOutputStream getOutputStream() {
222                 return pw.getStream();
223             }
224
225             @Override
226             public void sendError(int sc) throws IOException {
227                 super.sendError(sc);
228                 pw.setError(sc);
229
230             }
231
232             @Override
233             public void sendError(int sc, String msg) throws IOException {
234                 super.sendError(sc, msg);
235                 pw.setError(sc, msg);
236             }
237         };
238
239         try {
240             filterChain.doFilter(bufferedRequest, wrappedResp);
241         } catch (Exception e) {
242             log.error("Chain Exception", e);
243             throw e;
244         } finally {
245             try {
246                 byte[] bytes = baos.toByteArray();
247                 StringBuilder responseHeaders = new StringBuilder("RESPONSE HEADERS|");
248                 responseHeaders.append(formatResponseHeaders(response));
249                 responseHeaders.append("Status:");
250                 responseHeaders.append(response.getStatus());
251                 responseHeaders.append(";IsCommited:" + wrappedResp.isCommitted());
252
253                 log.info(responseHeaders.toString());
254
255                 if ("gzip".equals(response.getHeader("Content-Encoding"))) {
256                     log.info("UNGZIPED RESPONSE BODY|" + decompressGZIPByteArray(bytes));
257                 } else {
258                     log.info("RESPONSE BODY|" + new String(bytes));
259                 }
260
261                 if (pw.hasErrored()) {
262                     log.info("ERROR RESPONSE|" + pw.getErrorCode() + ":" + pw.getErrorMsg());
263                 } else {
264                     if (!wrappedResp.isCommitted()) {
265                         response.getOutputStream().write(bytes);
266                         response.getOutputStream().flush();
267                     }
268                 }
269             } catch (Exception e) {
270                 log.error("Exception in response filter", e);
271             }
272         }
273     }
274
275     @Override
276     public void destroy() {
277         // this method does nothing
278     }
279
280     private String decompressGZIPByteArray(byte[] bytes) {
281         BufferedReader in = null;
282         InputStreamReader inR = null;
283         ByteArrayInputStream byteS = null;
284         GZIPInputStream gzS = null;
285         StringBuilder str = new StringBuilder();
286         try {
287             byteS = new ByteArrayInputStream(bytes);
288             gzS = new GZIPInputStream(byteS);
289             inR = new InputStreamReader(gzS);
290             in = new BufferedReader(inR);
291
292             if (in != null) {
293                 String content;
294                 while ((content = in.readLine()) != null) {
295                     str.append(content);
296                 }
297             }
298
299         } catch (Exception e) {
300             log.error("Failed get read GZIPInputStream", e);
301         } finally {
302             if (byteS != null)
303                 try {
304                     byteS.close();
305                 } catch (IOException e1) {
306                     log.error("Failed to close ByteStream", e1);
307                 }
308             if (gzS != null)
309                 try {
310                     gzS.close();
311                 } catch (IOException e2) {
312                     log.error("Failed to close GZStream", e2);
313                 }
314             if (inR != null)
315                 try {
316                     inR.close();
317                 } catch (IOException e3) {
318                     log.error("Failed to close InputReader", e3);
319                 }
320             if (in != null)
321                 try {
322                     in.close();
323                 } catch (IOException e) {
324                     log.error("Failed to close BufferedReader", e);
325                 }
326         }
327         return str.toString();
328     }
329
330 }