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

org.glassfish.grizzly.http2.frames.SettingsFrame Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package org.glassfish.grizzly.http2.frames;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.glassfish.grizzly.Buffer;
import org.glassfish.grizzly.Grizzly;
import org.glassfish.grizzly.ThreadCache;
import org.glassfish.grizzly.http.util.BufferChunk;
import org.glassfish.grizzly.http.util.ByteChunk;
import org.glassfish.grizzly.http.util.DataChunk;
import org.glassfish.grizzly.memory.MemoryManager;

/**
 * TODO: Need to implement handling of per-setting flags.
 */
public class SettingsFrame extends Http2Frame {
    private static final Logger LOGGER = Grizzly.logger(SettingsFrame.class);

    private static final char[] CA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".toCharArray();
    private static final int[] IA = new int[256];
    static {
        Arrays.fill(IA, -1);
        for (int i = 0, iS = CA.length; i < iS; i++) {
            IA[CA[i]] = i;
        }
    }

    private static final ThreadCache.CachedTypeIndex CACHE_IDX = ThreadCache.obtainIndex(SettingsFrame.class, 8);

    private static final String[] OPTION_TEXT = { "HEADER_TABLE_SIZE", "ENABLE_PUSH", "MAX_CONCURRENT_STREAMS", "INITIAL_WINDOW_SIZE", "MAX_FRAME_SIZE",
            "MAX_HEADER_LIST_SIZE" };

    public static final int TYPE = 4;

    public static final byte ACK_FLAG = 0x01;

    static final Map FLAG_NAMES_MAP = new HashMap<>(2);

    static {
        FLAG_NAMES_MAP.put((int) ACK_FLAG, "ACK");
    }

    public static final int MAX_DEFINED_SETTINGS = 6;
    /*
     * Values defined by SETTINGS are the index in settingsSlots for their respective values.
     */
    public static final int SETTINGS_HEADER_TABLE_SIZE = 1;
    public static final int SETTINGS_ENABLE_PUSH = 2;
    public static final int SETTINGS_MAX_CONCURRENT_STREAMS = 3;
    public static final int SETTINGS_INITIAL_WINDOW_SIZE = 4;
    public static final int SETTINGS_MAX_FRAME_SIZE = 5;
    public static final int SETTINGS_MAX_HEADER_LIST_SIZE = 6;

    private int numberOfSettings;

    private final Setting[] settings = new Setting[MAX_DEFINED_SETTINGS];

    // ------------------------------------------------------------ Constructors

    private SettingsFrame() {
        for (int i = 0; i < MAX_DEFINED_SETTINGS; i++) {
            settings[i] = new Setting();
        }
    }

    // ---------------------------------------------------------- Public Methods

    static SettingsFrame create() {
        SettingsFrame frame = ThreadCache.takeFromCache(CACHE_IDX);
        if (frame == null) {
            frame = new SettingsFrame();
        }
        return frame;
    }

    public static SettingsFrame fromBuffer(final int flags, final int streamId, final Buffer frameBuffer) {

        SettingsFrame frame = create();
        frame.setStreamId(streamId);
        frame.setFlags(flags);
        frame.setFrameBuffer(frameBuffer);
        if (frameBuffer.remaining() % 6 == 0) {
            while (frameBuffer.hasRemaining()) {
                frame.addSetting(frameBuffer.getShort(), frameBuffer.getInt());
            }
        } else {
            frame.numberOfSettings = -1;
        }

        return frame;
    }

    @Override
    public int getType() {
        return TYPE;
    }

    public static SettingsFrame fromBase64Uri(final DataChunk src) {
        if (src.getType() == DataChunk.Type.Bytes) {
            final ByteChunk bc = src.getByteChunk();
            return parseBase64Uri(bc.getBuffer(), bc.getStart(), bc.getEnd());
        } else if (src.getType() == DataChunk.Type.Buffer) {
            final BufferChunk bc = src.getBufferChunk();
            return parseBase64Uri(bc.getBuffer(), bc.getStart(), bc.getEnd());
        }

        return parseBase64Uri(src.toString());
    }

    private static SettingsFrame parseBase64Uri(final byte[] bytes, int offs, final int end) {
        final SettingsFrame frame = new SettingsFrame();

        while (offs < end) {
            frame.addBase64UriSetting(IA[bytes[offs++]], IA[bytes[offs++]], IA[bytes[offs++]], IA[bytes[offs++]], IA[bytes[offs++]], IA[bytes[offs++]],
                    IA[bytes[offs++]], IA[bytes[offs++]]);
        }

        return frame;
    }

    private static SettingsFrame parseBase64Uri(final Buffer buffer, int offs, final int end) {
        final SettingsFrame frame = new SettingsFrame();

        while (offs < end) {
            frame.addBase64UriSetting(IA[buffer.get(offs++)], IA[buffer.get(offs++)], IA[buffer.get(offs++)], IA[buffer.get(offs++)], IA[buffer.get(offs++)],
                    IA[buffer.get(offs++)], IA[buffer.get(offs++)], IA[buffer.get(offs++)]);
        }

        return frame;
    }

    private static SettingsFrame parseBase64Uri(final String s) {
        final SettingsFrame frame = new SettingsFrame();

        int offs = 0;
        final int end = s.length();

        while (offs < end) {
            frame.addBase64UriSetting(IA[s.charAt(offs++)], IA[s.charAt(offs++)], IA[s.charAt(offs++)], IA[s.charAt(offs++)], IA[s.charAt(offs++)],
                    IA[s.charAt(offs++)], IA[s.charAt(offs++)], IA[s.charAt(offs++)]);
        }

        return frame;
    }

    public static SettingsFrameBuilder builder() {
        return new SettingsFrameBuilder();
    }

    public boolean isAck() {
        return isFlagSet(ACK_FLAG);
    }

    public int getNumberOfSettings() {
        return numberOfSettings;
    }

    public Setting getSettingByIndex(final int idx) {
        return idx >= 0 && idx < numberOfSettings ? settings[idx] : null;
    }

    public String toBase64Uri() {
        if (numberOfSettings == 0) {
            return "";
        }

        final StringBuilder sb = new StringBuilder(numberOfSettings * 8);
        for (int i = 0; i < numberOfSettings; i++) {
            final int id = settings[i].id;
            final int value = settings[i].value;

            threeBytesToBase64Uri(id >> 8 & 0xFF, id & 0xFF, value >>> 24, sb);

            threeBytesToBase64Uri(value >> 16 & 0xFF, value >> 8 & 0xFF, value & 0xFF, sb);
        }

        return sb.toString();
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        sb.append("SettingsFrame {").append(headerToString()).append(", numberOfSettings=").append(numberOfSettings);
        if (numberOfSettings > 0) {
            sb.append(", [");
            for (int i = 0; i < numberOfSettings; i++) {
                sb.append(' ');
                sb.append(OPTION_TEXT[settings[i].id - 1]).append('=').append(settings[i].value);
            }
        }
        sb.append(" ]}");
        return sb.toString();
    }

    @Override
    protected int calcLength() {
        if (numberOfSettings == -1) {
            // invalid settings frame
            return frameBuffer.remaining();
        }
        return numberOfSettings * 6;
    }

    public String getSettingNameById(final int id) {
        return OPTION_TEXT[id - 1];
    }

    // -------------------------------------------------- Methods from Cacheable

    @Override
    public void recycle() {
        if (DONT_RECYCLE) {
            return;
        }

        numberOfSettings = 0;
        super.recycle();
        ThreadCache.putToCache(CACHE_IDX, this);
    }

    // -------------------------------------------------- Methods from Http2Frame

    @Override
    public Buffer toBuffer(final MemoryManager memoryManager) {

        final Buffer buffer = memoryManager.allocate(FRAME_HEADER_SIZE + numberOfSettings * 6);

        serializeFrameHeader(buffer);

        if (numberOfSettings > 0) {
            for (int i = 0; i < numberOfSettings; i++) {
                final Setting setting = settings[i];

                buffer.putShort((short) setting.id);
                buffer.putInt(setting.value);
            }
        }

        buffer.trim();
        return buffer;
    }

    @Override
    protected Map getFlagNamesMap() {
        return FLAG_NAMES_MAP;
    }

    // ------------------------------------------------------- Private Methods

    private void threeBytesToBase64Uri(final int b1, final int b2, final int b3, final StringBuilder to) {
        to.append(CA[b1 >>> 2]).append(CA[(b1 & 3) << 4 | b2 >>> 4]).append(CA[(b2 & 15) << 2 | b3 >>> 6]).append(CA[b3 & 63]);
    }

    private void addBase64UriSetting(final int b1, final int b2, final int b3, final int b4, final int b5, final int b6, final int b7, final int b8) {

        if (b1 == -1 || b2 == -1 || b3 == -1 || b4 == -1 || b5 == -1 || b6 == -1 || b7 == -1 || b8 == -1) {
            throw new IllegalStateException("Unknown base64uri character");
        }

        final int tmp1 = b1 << 18 | b2 << 12 | b3 << 6 | b4;
        final int tmp2 = b5 << 18 | b6 << 12 | b7 << 6 | b8;

        final int setting = tmp1 >> 8;
        final int value = (tmp1 & 0xFF) << 24 | tmp2;

        addSetting(setting, value);
    }

    private void addSetting(final int settingId, final int value) {
        if (settingId > 0 && settingId <= MAX_DEFINED_SETTINGS) {
            final int oldIdx = idx(settingId);
            if (oldIdx != -1) {
                numberOfSettings--; // we will remove the old value and add a new one
                if (oldIdx < numberOfSettings) {
                    // shift settings by one
                    final Setting oldSetting = settings[oldIdx];
                    System.arraycopy(settings, oldIdx + 1, settings, oldIdx, numberOfSettings - oldIdx);
                    settings[numberOfSettings] = oldSetting;
                }
            }

            final Setting storedSetting = settings[numberOfSettings++];
            storedSetting.id = settingId;
            storedSetting.value = value;
        } else {
            LOGGER.log(Level.WARNING, "Setting {0} is unknown and will be ignored", settingId);
        }
    }

    private int idx(final int settingId) {
        for (int i = 0; i < numberOfSettings; i++) {
            if (settings[i].id == settingId) {
                return i;
            }
        }

        return -1;
    }
    // ---------------------------------------------------------- Nested Classes

    public static class SettingsFrameBuilder extends Http2FrameBuilder {

        private int numberOfSettings;
        private final Setting[] settings = new Setting[MAX_DEFINED_SETTINGS];

        // -------------------------------------------------------- Constructors

        protected SettingsFrameBuilder() {
        }

        // ------------------------------------------------------ Public Methods

        public SettingsFrameBuilder setting(final int settingId, final int value) {
            if (settingId > 0 && settingId <= MAX_DEFINED_SETTINGS) {
                final Setting settingContainer;
                final int oldIdx = idx(settingId);
                if (oldIdx != -1) {
                    numberOfSettings--; // we will remove the old value and add a new one
                    if (oldIdx < numberOfSettings) {
                        // shift settings by one
                        final Setting oldSetting = settings[oldIdx];
                        System.arraycopy(settings, oldIdx + 1, settings, oldIdx, numberOfSettings - oldIdx);
                        settings[numberOfSettings++] = oldSetting;
                        settingContainer = oldSetting;
                    } else {
                        settingContainer = settings[numberOfSettings++];
                    }
                } else {
                    settingContainer = new Setting();
                    settings[numberOfSettings++] = settingContainer;
                }

                settingContainer.id = settingId;
                settingContainer.value = value;
            } else {
                LOGGER.log(Level.WARNING, "Setting {0} is unknown and will be ignored", settingId);
            }

            return this;
        }

        public SettingsFrameBuilder setAck() {
            setFlag(ACK_FLAG);
            return this;
        }

        @Override
        public SettingsFrame build() {
            final SettingsFrame frame = SettingsFrame.create();
            setHeaderValuesTo(frame);

            for (int i = 0; i < numberOfSettings; i++) {
                frame.addSetting(settings[i].id, settings[i].value);
            }

            return frame;
        }

        private int idx(final int settingId) {
            for (int i = 0; i < numberOfSettings; i++) {
                if (settings[i].id == settingId) {
                    return i;
                }
            }

            return -1;
        }

        // --------------------------------------- Methods from Http2FrameBuilder

        @Override
        protected SettingsFrameBuilder getThis() {
            return this;
        }

    } // END SettingsFrameBuilder

    public static final class Setting {
        private int id;
        private int value;

        private Setting() {
        }

        public int getId() {
            return id;
        }

        public int getValue() {
            return value;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy