All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.wink.server.internal.log.Requests Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.wink.server.internal.log;

import java.io.IOException;
import java.util.Properties;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MultivaluedMap;

import org.apache.wink.server.handlers.HandlersChain;
import org.apache.wink.server.handlers.MessageContext;
import org.apache.wink.server.handlers.RequestHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Logs the incoming requests from client to server. This will log the headers
 * plus the containers.
 */
public class Requests implements RequestHandler {

    final private static Logger  logger             = LoggerFactory.getLogger(Requests.class);

    final private static int     BREAK_POINT        =
                                                        Integer
                                                            .valueOf(System
                                                                .getProperty(Requests.class
                                                                                 .getName() + ".breakPoint",
                                                                             "4096")).intValue();

    final private static boolean IS_LOGGED_AS_BYTES =
                                                        Boolean
                                                            .valueOf(System
                                                                .getProperty(Requests.class
                                                                                 .getName() + ".logAsBytes",
                                                                             "false"))
                                                            .booleanValue();

    final private static int     BUFFER_SIZE        =
                                                        Integer
                                                            .valueOf(System
                                                                .getProperty(Requests.class
                                                                                 .getName() + ".bufferSize",
                                                                             "8192")).intValue();

    public void init(Properties props) {
        /* do nothing */
    }

    public void handleRequest(MessageContext context, HandlersChain chain) throws Throwable {
        logger.trace("handleRequest({}, {}) entry", context, chain);
        try {
            if (logger.isDebugEnabled()) {
                logStartRequest(context);
            }
            chain.doChain(context);
        } finally {
            if (logger.isDebugEnabled()) {
                logFinishRequest(context);
            }
        }
        logger.trace("handleRequest({}, {}) exit", context, chain);
    }

    void logStartRequest(MessageContext context) {
        logger.trace("logStartRequest() entry");
        try {
            if (!logger.isDebugEnabled()) {
                logger.trace("logStartRequest() exit"); // how would this ever
                // occur?
                return;
            }
            
            /*
             * start wrapping the InputStream here in the hope that it will be
             * called during logFinishRequest
             */
            HttpServletRequestWrapper request =
                context.getAttribute(HttpServletRequestWrapper.class);
            if (request == null) {
                logger.debug("Could not find the HTTP Servlet Request to wrap.");
                // this is a really bad path that should probably be an error,
                // but just in case
                logger.trace("logStartRequest() exit");
                return;
            }
            
            logger.debug("Request URI is " + context.getUriInfo().getRequestUri().toASCIIString());
            HttpHeaders headers = context.getHttpHeaders();
            MultivaluedMap headersMap = headers.getRequestHeaders();
            String headersString = "{";
            for(String key : headersMap.keySet()) {
                headersString += (key + "=" + headersMap.get(key) + ",");
            }
            if(headersString.length() > 1)
                headersString = headersString.substring(0, headersString.length() -1);
            headersString += "}";
            logger.debug("HTTP Headers are " + headersString);
            
            RequestWrapper wrapper = new RequestWrapper(request);
            context.setAttribute(RequestWrapper.class, wrapper);
            context.setAttribute(HttpServletRequest.class, wrapper);
            context.setAttribute(HttpServletRequestWrapper.class, wrapper);
        } catch (Exception e) {
            logger.trace("Could not log the start of the request", e);
        }
        logger.trace("logStartRequest() exit");
    }

    void logFinishRequest(MessageContext context) {
        logger.trace("logFinishRequest() entry");
        try {
            if (!logger.isDebugEnabled()) {
                logger.trace("logFinishRequest() exit"); // how would this ever
                // occur?
                return;
            }
            RequestWrapper requestWrapper = context.getAttribute(RequestWrapper.class);
            if (requestWrapper == null) {
                logger.debug("Did not find the RequestWrapper so will not log the request entity.");
                logger.trace("logStartRequest() exit");
                return;
            }
            LoggedServletInputStream loggedInputStream = requestWrapper.getLoggedInputStream();
            if (loggedInputStream == null || loggedInputStream.getLoggedByteBufferLength() == 0) {
                logger
                    .debug("The request entity was not read from the HttpServletRequest.getInputStream().");
                return;
            }
            byte[] buffer = loggedInputStream.getLoggedByteBuffer();
            final int bufferLength = loggedInputStream.getLoggedByteBufferLength();

            if (IS_LOGGED_AS_BYTES) {
                logger.debug("The request entity as bytes:");
                StringBuffer sb = new StringBuffer();
                int outputCount = 0;

                for (int count = 0; count < bufferLength; ++count) {
                    sb.append(String.format("%#04x ", buffer[count]));
                    sb.append(" ");
                    ++outputCount;
                    if (outputCount > BREAK_POINT) {
                        /*
                         * 100KB increments
                         */
                        logger.debug("{}", sb);
                        sb = new StringBuffer();
                        outputCount = 0;
                    }
                }
                if (outputCount > 0) {
                    logger.debug("{}", sb);
                    sb = new StringBuffer();
                }
            } else {
                logger.debug("The request entity as a String in the default encoding:");
                int offset = 0;
                while (offset < bufferLength) {
                    int length = bufferLength - offset;
                    if (length > BREAK_POINT) {
                        length = BREAK_POINT;
                    }
                    String str = new String(buffer, offset, length);
                    logger.debug("{}", str);
                    offset += length;
                }
            }
            /*
             * remove it in case during a Response chain, an exception is thrown
             * and then a Request needs to be logged again. so only 1 shot at
             * logging a request.
             */
            context.setAttribute(RequestWrapper.class, null);
        } catch (Exception e) {
            logger.debug("Could not log the finishing of the request", e);
        }
        logger.trace("logFinishRequest() exit");
    }

    public static class RequestWrapper extends HttpServletRequestWrapper {
        final private HttpServletRequest request;

        private LoggedServletInputStream inputStreamLogger            = null;

        private ServletInputStream       originalStream;
        private boolean                  hasStreamBeenRetrievedBefore = false;

        public RequestWrapper(HttpServletRequest request) {
            super(request);
            this.request = request;
        }

        @Override
        public ServletInputStream getInputStream() throws IOException {
            if (inputStreamLogger != null) {
                return inputStreamLogger;
            }

            if (!hasStreamBeenRetrievedBefore) {
                originalStream = request.getInputStream();
            }
            if (originalStream == null) {
                /*
                 * this could happen in a weird situation with the web
                 * container. defensive coding for now.
                 */
                logger
                    .debug("The web container did not return a stream from HttpServletRequest.getInputStream()");
                return null;
            }

            inputStreamLogger = new LoggedServletInputStream(originalStream, BUFFER_SIZE);
            return inputStreamLogger;
        }

        public LoggedServletInputStream getLoggedInputStream() {
            return inputStreamLogger;
        }
    }

    /**
     * A wrapper class for a ServletInputStream that also logs the bytes.
     */
    public static class LoggedServletInputStream extends ServletInputStream {

        final private ServletInputStream originalRequest;

        private final byte[]             requestBuffer;

        private int                      offset = 0;

        public LoggedServletInputStream(ServletInputStream originalRequest, int bufferSize) {
            this.originalRequest = originalRequest;
            requestBuffer = new byte[bufferSize];
        }

        @Override
        public int available() throws IOException {
            return originalRequest.available();
        }

        @Override
        public void close() throws IOException {
            originalRequest.close();
        }

        @Override
        public void mark(int readlimit) {
            originalRequest.mark(readlimit);
        }

        @Override
        public boolean markSupported() {
            return originalRequest.markSupported();
        }

        @Override
        public int read() throws IOException {
            int value = originalRequest.read();

            if (value < 0) {
                return value;
            }

            if (offset < requestBuffer.length - 1) {
                requestBuffer[offset] = (byte)value;
                ++offset;
            }
            return value;
        }

        @Override
        public int read(byte[] b) throws IOException {
            int value = originalRequest.read(b);

            if (value < 0) {
                return value;
            }

            int diff = value;
            if (offset + value > requestBuffer.length) {
                diff = requestBuffer.length - offset;
            }

            if (diff > 0) {
                System.arraycopy(b, 0, requestBuffer, offset, diff);
                offset += diff;
            }
            return value;
        }

        @Override
        public int read(byte[] b, int off, int len) throws IOException {
            int value = originalRequest.read(b, off, len);

            if (value < 0) {
                return value;
            }

            int diff = value;
            if (offset + value > requestBuffer.length) {
                diff = requestBuffer.length - offset;
            }

            if (diff > 0) {
                System.arraycopy(b, off, requestBuffer, offset, diff);
                offset += diff;
            }

            return value;
        }

        @Override
        public int readLine(byte[] b, int off, int len) throws IOException {
            int value = originalRequest.readLine(b, off, len);

            if (value < 0) {
                return value;
            }

            int diff = value;
            if (offset + value > requestBuffer.length) {
                diff = requestBuffer.length - offset;
            }

            if (diff > 0) {
                System.arraycopy(b, off, requestBuffer, offset, diff);
                offset += diff;
            }

            return value;
        }

        @Override
        public void reset() throws IOException {
            originalRequest.reset();
        }

        @Override
        public long skip(long n) throws IOException {
            return originalRequest.skip(n);
        }

        public byte[] getLoggedByteBuffer() {
            return requestBuffer;
        }

        public int getLoggedByteBufferLength() {
            return offset;
        }

    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy