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

com.netflix.zuul.message.ZuulMessageImpl 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.message;

import com.google.common.base.Charsets;
import com.google.common.base.Strings;
import com.netflix.config.DynamicIntProperty;
import com.netflix.config.DynamicPropertyFactory;
import com.netflix.zuul.context.SessionContext;
import com.netflix.zuul.filters.ZuulFilter;
import com.netflix.zuul.message.http.HttpHeaderNames;
import io.netty.buffer.*;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.DefaultHttpContent;
import io.netty.handler.codec.http.DefaultLastHttpContent;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.LastHttpContent;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;

import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

/**
 * User: [email protected]
 * Date: 2/20/15
 * Time: 3:10 PM
 */
public class ZuulMessageImpl implements ZuulMessage
{
    protected static final DynamicIntProperty MAX_BODY_SIZE_PROP = DynamicPropertyFactory.getInstance().getIntProperty(
            "zuul.message.body.max.size", 25 * 1000 * 1024);
    private static final Charset CS_UTF8 = Charset.forName("UTF-8");

    protected final SessionContext context;
    protected Headers headers;

    private boolean hasBody;
    private boolean bodyBufferedCompletely;
    private List bodyChunks;


    public ZuulMessageImpl(SessionContext context) {
        this(context, new Headers());
    }

    public ZuulMessageImpl(SessionContext context, Headers headers) {
        this.context = context == null ? new SessionContext() : context;
        this.headers = headers == null ? new Headers() : headers;
        this.bodyChunks = new ArrayList<>(16);
    }

    @Override
    public SessionContext getContext() {
        return context;
    }

    @Override
    public Headers getHeaders() {
        return headers;
    }

    @Override
    public void setHeaders(Headers newHeaders) {
        this.headers = newHeaders;
    }

    @Override
    public int getMaxBodySize() {
        return MAX_BODY_SIZE_PROP.get();
    }


    @Override
    public void setHasBody(boolean hasBody) {
        this.hasBody = hasBody;
    }

    @Override
    public boolean hasBody() {
        return hasBody;
    }

    @Override
    public boolean hasCompleteBody() {
        return bodyBufferedCompletely;
    }

    @Override
    public void bufferBodyContents(final HttpContent chunk) {
        setHasBody(true);
        bodyChunks.add(chunk);
        if (chunk instanceof  LastHttpContent) {
            bodyBufferedCompletely = true;
        }
    }

    private void setContentLength(int length) {
        headers.remove(HttpHeaderNames.TRANSFER_ENCODING);
        headers.set(HttpHeaderNames.CONTENT_LENGTH, Integer.toString(length));
    }

    @Override
    public void setBodyAsText(String bodyText) {
        disposeBufferedBody();
        if (! Strings.isNullOrEmpty(bodyText)) {
            final ByteBuf content = Unpooled.copiedBuffer(bodyText.getBytes(Charsets.UTF_8));
            bufferBodyContents(new DefaultLastHttpContent(content));
            setContentLength(bodyText.getBytes(CS_UTF8).length);
        } else {
            bufferBodyContents(new DefaultLastHttpContent());
            setContentLength(0);
        }
    }

    @Override
    public void setBody(byte[] body) {
        disposeBufferedBody();
        if (body != null && body.length > 0) {
            final ByteBuf content = Unpooled.copiedBuffer(body);
            bufferBodyContents(new DefaultLastHttpContent(content));
            setContentLength(body.length);
        } else {
            bufferBodyContents(new DefaultLastHttpContent());
            setContentLength(0);
        }
    }

    @Override
    public String getBodyAsText() {
        final byte[] body = getBody();
        return (body != null && body.length > 0) ? new String(getBody(), Charsets.UTF_8) : null;
    }

    @Override
    public byte[] getBody() {
        if (bodyChunks.size() == 0) {
            return null;
        }

        int size = 0;
        for (final HttpContent chunk : bodyChunks) {
            size += chunk.content().readableBytes();
        }
        final byte[] body = new byte[size];
        int offset = 0;
        for (final HttpContent chunk : bodyChunks) {
            final ByteBuf content = chunk.content();
            final int len = content.readableBytes();
            content.getBytes(content.readerIndex(), body, offset, len);
            offset += len;
        }
        return body;
    }

    @Override
    public int getBodyLength() {
        int size = 0;
        for (final HttpContent chunk : bodyChunks) {
            size += chunk.content().readableBytes();
        }
        return size;
    }

    @Override
    public Iterable getBodyContents() {
        return Collections.unmodifiableList(bodyChunks);
    }

    @Override
    public boolean finishBufferedBodyIfIncomplete() {
        if (! bodyBufferedCompletely) {
            bufferBodyContents(new DefaultLastHttpContent());
            return true;
        }
        return false;
    }

    @Override
    public void disposeBufferedBody() {
        bodyChunks.forEach(chunk -> {
            if ((chunk != null) && (chunk.refCnt() > 0)) {
                chunk.release();
            }
        });
        bodyChunks.clear();
    }

    @Override
    public void runBufferedBodyContentThroughFilter(ZuulFilter filter) {
        //Loop optimized for the common case: Most filters' processContentChunk() return
        // original chunk passed in as is without any processing
        for (int i=0; i < bodyChunks.size(); i++) {
            final HttpContent origChunk = bodyChunks.get(i);
            final HttpContent filteredChunk = filter.processContentChunk(this, origChunk);
            if ((filteredChunk != null) && (filteredChunk != origChunk)) {
                //filter actually did some processing, set the new chunk in and release the old chunk.
                bodyChunks.set(i, filteredChunk);
                final int refCnt = origChunk.refCnt();
                if (refCnt > 0) {
                    origChunk.release(refCnt);
                }
            }
        }
    }

    @Override
    public ZuulMessage clone() {
        final ZuulMessageImpl copy = new ZuulMessageImpl(context.clone(), headers.clone());
        this.bodyChunks.forEach(chunk -> {
            chunk.retain();
            copy.bufferBodyContents(chunk);
        });
        return copy;
    }

    /**
     * Override this in more specific subclasses to add request/response info for logging purposes.
     *
     * @return
     */
    @Override
    public String getInfoForLogging()
    {
        return "ZuulMessage";
    }

    @RunWith(MockitoJUnitRunner.class)
    public static class UnitTest
    {
        @Test
        public void testClone()
        {
            SessionContext ctx1 = new SessionContext();
            ctx1.set("k1", "v1");
            Headers headers1 = new Headers();
            headers1.set("k1", "v1");

            ZuulMessage msg1 = new ZuulMessageImpl(ctx1, headers1);
            ZuulMessage msg2 = msg1.clone();

            assertEquals(msg1.getBodyAsText(), msg2.getBodyAsText());
            assertEquals(msg1.getHeaders(), msg2.getHeaders());
            assertEquals(msg1.getContext(), msg2.getContext());

            // Verify that values of the 2 messages are decoupled.
            msg1.getHeaders().set("k1", "v_new");
            msg1.getContext().set("k1", "v_new");

            assertEquals("v1", msg2.getHeaders().getFirst("k1"));
            assertEquals("v1", msg2.getContext().get("k1"));
        }

        @Test
        public void testBufferBody2GetBody() {
            final ZuulMessage msg = new ZuulMessageImpl(new SessionContext(), new Headers());
            msg.bufferBodyContents(new DefaultHttpContent(Unpooled.copiedBuffer("Hello ".getBytes())));
            msg.bufferBodyContents(new DefaultLastHttpContent(Unpooled.copiedBuffer("World!".getBytes())));
            final String body = new String(msg.getBody());
            assertTrue(msg.hasBody());
            assertTrue(msg.hasCompleteBody());
            assertEquals("Hello World!", body);
        }

        @Test
        public void testBufferBody3GetBody() {
            final ZuulMessage msg = new ZuulMessageImpl(new SessionContext(), new Headers());
            msg.bufferBodyContents(new DefaultHttpContent(Unpooled.copiedBuffer("Hello ".getBytes())));
            msg.bufferBodyContents(new DefaultHttpContent(Unpooled.copiedBuffer("World!".getBytes())));
            msg.bufferBodyContents(new DefaultLastHttpContent());
            final String body = new String(msg.getBody());
            assertTrue(msg.hasBody());
            assertTrue(msg.hasCompleteBody());
            assertEquals("Hello World!", body);
        }

        @Test
        public void testBufferBody3GetBodyAsText() {
            final ZuulMessage msg = new ZuulMessageImpl(new SessionContext(), new Headers());
            msg.bufferBodyContents(new DefaultHttpContent(Unpooled.copiedBuffer("Hello ".getBytes())));
            msg.bufferBodyContents(new DefaultHttpContent(Unpooled.copiedBuffer("World!".getBytes())));
            msg.bufferBodyContents(new DefaultLastHttpContent());
            final String body = msg.getBodyAsText();
            assertTrue(msg.hasBody());
            assertTrue(msg.hasCompleteBody());
            assertEquals("Hello World!", body);
        }

        @Test
        public void testSetBodyGetBody() {
            final ZuulMessage msg = new ZuulMessageImpl(new SessionContext(), new Headers());
            msg.setBody("Hello World!".getBytes());
            final String body = new String(msg.getBody());
            assertEquals("Hello World!", body);
        }

        @Test
        public void testSetBodyAsTextGetBody() {
            final ZuulMessage msg = new ZuulMessageImpl(new SessionContext(), new Headers());
            msg.setBodyAsText("Hello World!");
            final String body = new String(msg.getBody());
            assertTrue(msg.hasBody());
            assertTrue(msg.hasCompleteBody());
            assertEquals("Hello World!", body);
        }

        @Test
        public void testSetBodyAsTextGetBodyAsText() {
            final ZuulMessage msg = new ZuulMessageImpl(new SessionContext(), new Headers());
            msg.setBodyAsText("Hello World!");
            final String body = msg.getBodyAsText();
            assertTrue(msg.hasBody());
            assertTrue(msg.hasCompleteBody());
            assertEquals("Hello World!", body);
        }

        @Test
        public void testMultiSetBodyAsTextGetBody() {
            final ZuulMessage msg = new ZuulMessageImpl(new SessionContext(), new Headers());
            msg.setBodyAsText("Hello World!");
            String body = new String(msg.getBody());
            assertTrue(msg.hasBody());
            assertTrue(msg.hasCompleteBody());
            assertEquals("Hello World!", body);
            msg.setBodyAsText("Goodbye World!");
            body = new String(msg.getBody());
            assertTrue(msg.hasBody());
            assertTrue(msg.hasCompleteBody());
            assertEquals("Goodbye World!", body);
        }

        @Test
        public void testMultiSetBodyGetBody() {
            final ZuulMessage msg = new ZuulMessageImpl(new SessionContext(), new Headers());
            msg.setBody("Hello World!".getBytes());
            String body = new String(msg.getBody());
            assertTrue(msg.hasBody());
            assertTrue(msg.hasCompleteBody());
            assertEquals("Hello World!", body);
            msg.setBody("Goodbye World!".getBytes());
            body = new String(msg.getBody());
            assertTrue(msg.hasBody());
            assertTrue(msg.hasCompleteBody());
            assertEquals("Goodbye World!", body);
        }

    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy