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

com.netflix.zuul.context.Debug Maven / Gradle / Ivy

There is a newer version: 2.5.13
Show newest version
/*
 * Copyright 2018 Netflix, Inc.
 *
 *      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 com.netflix.zuul.context;

import com.netflix.zuul.message.Header;
import com.netflix.zuul.message.Headers;
import com.netflix.zuul.message.ZuulMessage;
import com.netflix.zuul.message.http.*;
import com.netflix.zuul.util.HttpUtils;
import org.apache.commons.io.IOUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import rx.Observable;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.zip.GZIPInputStream;

import static org.junit.Assert.*;

/**
 * Simple wrapper class around the RequestContext for setting and managing Request level Debug data.
 * @author Mikey Cohen
 * Date: 1/25/12
 * Time: 2:26 PM
 */
public class Debug {
    private static final Logger LOG = LoggerFactory.getLogger(Debug.class);

    public static void setDebugRequest(SessionContext ctx, boolean bDebug) {
        ctx.setDebugRequest(bDebug);
    }

    public static void setDebugRequestHeadersOnly(SessionContext ctx, boolean bHeadersOnly) {
        ctx.setDebugRequestHeadersOnly(bHeadersOnly);
    }

    public static boolean debugRequestHeadersOnly(SessionContext ctx) {
        return ctx.debugRequestHeadersOnly();
    }


    public static void setDebugRouting(SessionContext ctx, boolean bDebug) {
        ctx.setDebugRouting(bDebug);
    }


    public static boolean debugRequest(SessionContext ctx) {
        return ctx.debugRequest();
    }

    public static boolean debugRouting(SessionContext ctx) {
        return ctx.debugRouting();
    }

    public static void addRoutingDebug(SessionContext ctx, String line) {
        List rd = getRoutingDebug(ctx);
        rd.add(line);
    }

    public static void addRequestDebugForMessage(SessionContext ctx, ZuulMessage message, String prefix)
    {
        for (Header header : message.getHeaders().entries()) {
            Debug.addRequestDebug(ctx, prefix + " " + header.getKey() + " " + header.getValue());
        }

        if (message.hasBody()) {
            String bodyStr = message.getBodyAsText();
            Debug.addRequestDebug(ctx, prefix + " " + bodyStr);
        }
    }

    /**
     *
     * @return Returns the list of routiong debug messages
     */
    public static List getRoutingDebug(SessionContext ctx) {
        List rd = (List) ctx.get("routingDebug");
        if (rd == null) {
            rd = new ArrayList();
            ctx.set("routingDebug", rd);
        }
        return rd;
    }

    /**
     * Adds a line to the  Request debug messages
     * @param line
     */
    public static void addRequestDebug(SessionContext ctx, String line) {
        List rd = getRequestDebug(ctx);
        rd.add(line);
    }

    /**
     *
     * @return returns the list of request debug messages
     */
    public static List getRequestDebug(SessionContext ctx) {
        List rd = (List) ctx.get("requestDebug");
        if (rd == null) {
            rd = new ArrayList();
            ctx.set("requestDebug", rd);
        }
        return rd;
    }


    /**
     * Adds debug details about changes that a given filter made to the request context.
     * @param filterName
     * @param copy
     */
    public static void compareContextState(String filterName, SessionContext context, SessionContext copy) {
        // TODO - only comparing Attributes. Need to compare the messages too.
        Iterator it = context.keySet().iterator();
        String key = it.next();
        while (key != null) {
            if ((!key.equals("routingDebug") && !key.equals("requestDebug"))) {
                Object newValue = context.get(key);
                Object oldValue = copy.get(key);
                if (oldValue == null && newValue != null) {
                    addRoutingDebug(context, "{" + filterName + "} added " + key + "=" + newValue.toString());
                } else if (oldValue != null && newValue != null) {
                    if (!(oldValue.equals(newValue))) {
                        addRoutingDebug(context, "{" +filterName + "} changed " + key + "=" + newValue.toString());
                    }
                }
            }
            if (it.hasNext()) {
                key = it.next();
            } else {
                key = null;
            }
        }

    }

    public static Observable writeDebugRequest(SessionContext context,
                                                                HttpRequestInfo request, boolean isInbound)
    {
        Observable obs = null;
        if (Debug.debugRequest(context)) {
            String prefix = isInbound ? "REQUEST_INBOUND" : "REQUEST_OUTBOUND";
            String arrow = ">";

            Debug.addRequestDebug(context, String.format("%s:: %s LINE: %s %s %s",
                    prefix, arrow, request.getMethod().toUpperCase(), request.getPathAndQuery(), request.getProtocol()));
            obs = Debug.writeDebugMessage(context, request, prefix, arrow);
        }

        if (obs == null)
            obs = Observable.just(Boolean.FALSE);

        return obs;
    }

    public static Observable writeDebugResponse(SessionContext context,
                                                                  HttpResponseInfo response, boolean isInbound)
    {
        Observable obs = null;
        if (Debug.debugRequest(context)) {
            String prefix = isInbound ? "RESPONSE_INBOUND" : "RESPONSE_OUTBOUND";
            String arrow = "<";

            Debug.addRequestDebug(context, String.format("%s:: %s STATUS: %s", prefix, arrow, response.getStatus()));
            obs = Debug.writeDebugMessage(context, response, prefix, arrow);
        }

        if (obs == null)
            obs = Observable.just(Boolean.FALSE);

        return obs;
    }

    public static Observable writeDebugMessage(SessionContext context, ZuulMessage msg,
                                                            String prefix, String arrow)
    {
        Observable obs = null;

        for (Header header : msg.getHeaders().entries()) {
            Debug.addRequestDebug(context, String.format("%s:: %s HDR: %s:%s", prefix, arrow, header.getKey(), header.getValue()));
        }

        // Capture the response body into a Byte array for later usage.
        if (msg.hasBody()) {
            if (! Debug.debugRequestHeadersOnly(context)) {
                // Convert body to a String and add to debug log.
                String body = msg.getBodyAsText();
                Debug.addRequestDebug(context, String.format("%s:: %s BODY: %s", prefix, arrow, body));
            }
        }

        if (obs == null)
            obs = Observable.just(Boolean.FALSE);

        return obs;
    }

    public static String bodyToText(byte[] bodyBytes, Headers headers)
    {
        try {
            if (HttpUtils.isGzipped(headers)) {
                GZIPInputStream gzIn = new GZIPInputStream(new ByteArrayInputStream(bodyBytes));
                bodyBytes = IOUtils.toByteArray(gzIn);
            }
            return IOUtils.toString(bodyBytes, "UTF-8");
        }
        catch (IOException e) {
            LOG.error("Error reading message body for debugging.", e);
            return "ERROR READING MESSAGE BODY!";
        }
    }


    @RunWith(MockitoJUnitRunner.class)
    public static class UnitTest
    {
        private SessionContext ctx;
        private Headers headers;
        private HttpQueryParams params;
        private HttpRequestMessage request;
        private HttpResponseMessage response;

        @Before
        public void setup() {
            ctx = new SessionContext();

            headers = new Headers();
            headers.add("lah", "deda");

            params = new HttpQueryParams();
            params.add("k1", "v1");

            request = new HttpRequestMessageImpl(ctx, "HTTP/1.1", "post", "/some/where",
                    params, headers, "9.9.9.9", "https", 80, "localhost");
            request.setBodyAsText("some text");
            request.storeInboundRequest();

            response = new HttpResponseMessageImpl(ctx, headers, request, 200);
            response.setBodyAsText("response text");
        }

        @Test
        public void testRequestDebug() {
            assertFalse(debugRouting(ctx));
            assertFalse(debugRequest(ctx));
            setDebugRouting(ctx, true);
            setDebugRequest(ctx, true);
            assertTrue(debugRouting(ctx));
            assertTrue(debugRequest(ctx));

            addRoutingDebug(ctx, "test1");
            assertTrue(getRoutingDebug(ctx).contains("test1"));

            addRequestDebug(ctx, "test2");
            assertTrue(getRequestDebug(ctx).contains("test2"));
        }

        @Test
        public void testWriteInboundRequestDebug()
        {
            ctx.setDebugRequest(true);
            ctx.setDebugRequestHeadersOnly(true);
            Debug.writeDebugRequest(ctx, request, true).toBlocking().single();

            List debugLines = Debug.getRequestDebug(ctx);
            assertEquals(3, debugLines.size());
            assertEquals("REQUEST_INBOUND:: > LINE: POST /some/where?k1=v1 HTTP/1.1", debugLines.get(0));
            assertEquals("REQUEST_INBOUND:: > HDR: Content-Length:13", debugLines.get(1));
            assertEquals("REQUEST_INBOUND:: > HDR: lah:deda", debugLines.get(2));
        }

        @Test
        public void testWriteOutboundRequestDebug()
        {
            ctx.setDebugRequest(true);
            ctx.setDebugRequestHeadersOnly(true);
            Debug.writeDebugRequest(ctx, request, false).toBlocking().single();

            List debugLines = Debug.getRequestDebug(ctx);
            assertEquals(3, debugLines.size());
            assertEquals("REQUEST_OUTBOUND:: > LINE: POST /some/where?k1=v1 HTTP/1.1", debugLines.get(0));
            assertEquals("REQUEST_OUTBOUND:: > HDR: Content-Length:13", debugLines.get(1));
            assertEquals("REQUEST_OUTBOUND:: > HDR: lah:deda", debugLines.get(2));
        }

        @Test
        public void testWriteRequestDebug_WithBody()
        {
            ctx.setDebugRequest(true);
            ctx.setDebugRequestHeadersOnly(false);
            Debug.writeDebugRequest(ctx, request, true).toBlocking().single();

            List debugLines = Debug.getRequestDebug(ctx);
            assertEquals(4, debugLines.size());
            assertEquals("REQUEST_INBOUND:: > LINE: POST /some/where?k1=v1 HTTP/1.1", debugLines.get(0));
            assertEquals("REQUEST_INBOUND:: > HDR: Content-Length:13", debugLines.get(1));
            assertEquals("REQUEST_INBOUND:: > HDR: lah:deda", debugLines.get(2));
            assertEquals("REQUEST_INBOUND:: > BODY: some text", debugLines.get(3));
        }

        @Test
        public void testWriteInboundResponseDebug()
        {
            ctx.setDebugRequest(true);
            ctx.setDebugRequestHeadersOnly(true);
            Debug.writeDebugResponse(ctx, response, true).toBlocking().single();

            List debugLines = Debug.getRequestDebug(ctx);
            assertEquals(3, debugLines.size());
            assertEquals("RESPONSE_INBOUND:: < STATUS: 200", debugLines.get(0));
            assertEquals("RESPONSE_INBOUND:: < HDR: Content-Length:13", debugLines.get(1));
            assertEquals("RESPONSE_INBOUND:: < HDR: lah:deda", debugLines.get(2));
        }

        @Test
        public void testWriteOutboundResponseDebug()
        {
            ctx.setDebugRequest(true);
            ctx.setDebugRequestHeadersOnly(true);
            Debug.writeDebugResponse(ctx, response, false).toBlocking().single();

            List debugLines = Debug.getRequestDebug(ctx);
            assertEquals(3, debugLines.size());
            assertEquals("RESPONSE_OUTBOUND:: < STATUS: 200", debugLines.get(0));
            assertEquals("RESPONSE_OUTBOUND:: < HDR: Content-Length:13", debugLines.get(1));
            assertEquals("RESPONSE_OUTBOUND:: < HDR: lah:deda", debugLines.get(2));
        }

        @Test
        public void testWriteResponseDebug_WithBody()
        {
            ctx.setDebugRequest(true);
            ctx.setDebugRequestHeadersOnly(false);
            Debug.writeDebugResponse(ctx, response, true).toBlocking().single();

            List debugLines = Debug.getRequestDebug(ctx);
            assertEquals(4, debugLines.size());
            assertEquals("RESPONSE_INBOUND:: < STATUS: 200", debugLines.get(0));
            assertEquals("RESPONSE_INBOUND:: < HDR: Content-Length:13", debugLines.get(1));
            assertEquals("RESPONSE_INBOUND:: < HDR: lah:deda", debugLines.get(2));
            assertEquals("RESPONSE_INBOUND:: < BODY: response text", debugLines.get(3));
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy