Author: markt Date: Tue Aug 18 16:29:47 2015 New Revision: 1696452 URL: http://svn.apache.org/r1696452 Log: Need different handling for local settings (settings the peer should be using to talk to us) and remote settings (settings we should be using to talk to the peer). Add a new class to handle local settings.
Added: tomcat/trunk/java/org/apache/coyote/http2/ConnectionSettingsLocal.java (with props) Added: tomcat/trunk/java/org/apache/coyote/http2/ConnectionSettingsLocal.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http2/ConnectionSettingsLocal.java?rev=1696452&view=auto ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http2/ConnectionSettingsLocal.java (added) +++ tomcat/trunk/java/org/apache/coyote/http2/ConnectionSettingsLocal.java Tue Aug 18 16:29:47 2015 @@ -0,0 +1,213 @@ +/* + * 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.coyote.http2; + +import java.util.HashMap; +import java.util.Map; + +/** + * Represents the local connection settingsL i.e. the settings the client is + * expected to use when communicating with the server. There will be a delay + * between calling a setter and the setting taking effect at the client. When a + * setter is called, the new value is added to the set of pending settings. Once + * the ACK is received, the new value is moved to the current settings. While + * waiting for the ACK, the getters will return the most lenient / generous / + * relaxed of the current setting and the pending setting. This class does not + * validate the values passed to the setters. If an invalid value is used the + * client will respond (almost certainly by closing the connection) as defined + * in the HTTP/2 specification. + */ +public class ConnectionSettingsLocal { + + private static final Integer KEY_HEADER_TABLE_SIZE = Integer.valueOf(1); + private static final Integer KEY_ENABLE_PUSH = Integer.valueOf(2); + private static final Integer KEY_MAX_CONCURRENT_STREAMS = Integer.valueOf(3); + private static final Integer KEY_INITAIL_WINDOW_SIZE = Integer.valueOf(4); + private static final Integer KEY_MAX_FRAME_SIZE = Integer.valueOf(5); + private static final Integer KEY_MAX_HEADER_LIST_SIZE = Integer.valueOf(6); + + private static final Long DEFAULT_HEADER_TABLE_SIZE = + Long.valueOf(ConnectionSettingsRemote.DEFAULT_HEADER_TABLE_SIZE); + private static final Long DEFAULT_ENABLE_PUSH = Long.valueOf(1); + private static final Long DEFAULT_MAX_CONCURRENT_STREAMS = + Long.valueOf(ConnectionSettingsRemote.UNLIMITED); + private static final Long DEFAULT_INITIAL_WINDOW_SIZE = + Long.valueOf(ConnectionSettingsRemote.DEFAULT_INITIAL_WINDOW_SIZE); + private static final Long DEFAULT_MAX_FRAME_SIZE = + Long.valueOf(ConnectionSettingsRemote.DEFAULT_MAX_FRAME_SIZE); + private static final Long DEFAULT_MAX_HEADER_LIST_SIZE = + Long.valueOf(ConnectionSettingsRemote.UNLIMITED); + + boolean sendInProgress = false; + + private Map<Integer,Long> current = new HashMap<>(); + private Map<Integer,Long> pending = new HashMap<>(); + + + public ConnectionSettingsLocal() { + // Set up the defaults + current.put(KEY_HEADER_TABLE_SIZE, DEFAULT_HEADER_TABLE_SIZE); + current.put(KEY_ENABLE_PUSH, DEFAULT_ENABLE_PUSH); + current.put(KEY_MAX_CONCURRENT_STREAMS, DEFAULT_MAX_CONCURRENT_STREAMS); + current.put(KEY_INITAIL_WINDOW_SIZE, DEFAULT_INITIAL_WINDOW_SIZE); + current.put(KEY_MAX_FRAME_SIZE, DEFAULT_MAX_FRAME_SIZE); + current.put(KEY_MAX_HEADER_LIST_SIZE, DEFAULT_MAX_HEADER_LIST_SIZE); + } + + + private synchronized void set(Integer key, long value) { + checkSend(); + if (current.get(key).longValue() == value) { + pending.remove(key); + } else { + pending.put(key, Long.valueOf(value)); + } + } + + + private synchronized long getMin(Integer key) { + Long pendingValue = pending.get(key); + long currentValue = current.get(key).longValue(); + if (pendingValue == null) { + return currentValue; + } else { + return Long.min(pendingValue.longValue(), currentValue); + } + } + + + private int getMinInt(Integer key) { + long result = getMin(key); + if (result > Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } else { + return (int) result; + } + } + + + private synchronized long getMax(Integer key) { + Long pendingValue = pending.get(key); + long currentValue = current.get(key).longValue(); + if (pendingValue == null) { + return currentValue; + } else { + return Long.max(pendingValue.longValue(), currentValue); + } + } + + + private int getMaxInt(Integer key) { + long result = getMax(key); + if (result > Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } else { + return (int) result; + } + } + + + synchronized byte[] getSettingsFrameForPending() { + checkSend(); + int payloadSize = pending.size() * 6; + byte[] result = new byte[9 + payloadSize]; + + ByteUtil.setThreeBytes(result, 0, payloadSize); + result[3] = FrameType.SETTINGS.getIdByte(); + // No flags + // Stream is zero + // Payload + int pos = 9; + for (Map.Entry<Integer,Long> setting : pending.entrySet()) { + ByteUtil.setTwoBytes(result, pos, setting.getKey().intValue()); + pos += 2; + ByteUtil.setFourBytes(result, pos, setting.getValue().longValue()); + pos += 4; + } + sendInProgress = true; + return result; + } + + + synchronized void ack() { + if (sendInProgress) { + sendInProgress = false; + current.putAll(pending); + pending.clear(); + } else { + // Unexpected ACK. Log it? + // TODO + } + } + + + private void checkSend() { + if (sendInProgress) { + // Coding error. No need for i18n + throw new IllegalStateException(); + } + } + + + public int getHeaderTableSize() { + return getMinInt(KEY_HEADER_TABLE_SIZE); + } + public void setHeaderTableSize(long headerTableSize) { + set(KEY_HEADER_TABLE_SIZE, headerTableSize); + } + + + public boolean getEnablePush() { + long result = getMin(KEY_ENABLE_PUSH); + return result != 0; + } + public void setEnablePush(long enablePush) { + set(KEY_ENABLE_PUSH, enablePush); + } + + + public long getMaxConcurrentStreams() { + return getMax(KEY_MAX_CONCURRENT_STREAMS); + } + public void setMaxConcurrentStreams(long maxConcurrentStreams) { + set(KEY_MAX_CONCURRENT_STREAMS, maxConcurrentStreams); + } + + + public int getInitialWindowSize() { + return getMaxInt(KEY_INITAIL_WINDOW_SIZE); + } + public void setInitialWindowSize(long initialWindowSize) { + set(KEY_INITAIL_WINDOW_SIZE, initialWindowSize); + } + + + public int getMaxFrameSize() { + return getMaxInt(KEY_MAX_FRAME_SIZE); + } + public void setMaxFrameSize(long maxFrameSize) { + set(KEY_MAX_FRAME_SIZE, maxFrameSize); + } + + + public long getMaxHeaderListSize() { + return getMax(KEY_MAX_HEADER_LIST_SIZE); + } + public void setMaxHeaderListSize(long maxHeaderListSize) { + set(KEY_MAX_HEADER_LIST_SIZE, maxHeaderListSize); + } +} Propchange: tomcat/trunk/java/org/apache/coyote/http2/ConnectionSettingsLocal.java ------------------------------------------------------------------------------ svn:eol-style = native --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org