/* * Copyright (C)2009 - SSHJ Contributors * * 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 net.schmizz.sshj.connection.channel; import net.schmizz.sshj.common.LoggerFactory; import net.schmizz.sshj.common.SSHRuntimeException; import net.schmizz.sshj.connection.ConnectionException; import org.slf4j.Logger; public abstract class Window { protected final Logger log; protected final Object lock = new Object(); protected final int maxPacketSize; protected long size; public Window(long initialWinSize, int maxPacketSize, LoggerFactory loggerFactory) { size = initialWinSize; this.maxPacketSize = maxPacketSize; log = loggerFactory.getLogger(getClass()); } public void expand(long inc) { synchronized (lock) { size += inc; log.debug("Increasing by {} up to {}", inc, size); lock.notifyAll(); } } public int getMaxPacketSize() { return maxPacketSize; } public long getSize() { synchronized (lock) { return size; } } public void consume(long dec) throws ConnectionException { synchronized (lock) { size -= dec; log.debug("Consuming by {} down to {}", dec, size); if (size < 0) throw new ConnectionException("Window consumed to below 0"); } } @Override public String toString() { return "[winSize=" + size + "]"; } /** Controls how much data we can send before an adjustment notification from remote end is required. */ public static final class Remote extends Window { public Remote(long initialWinSize, int maxPacketSize, LoggerFactory loggerFactory) { super(initialWinSize, maxPacketSize, loggerFactory); } public long awaitExpansion(long was) throws ConnectionException { synchronized (lock) { while (size <= was) { log.debug("Waiting, need size to grow from {} bytes", was); try { lock.wait(); } catch (InterruptedException ie) { throw new ConnectionException(ie); } } return size; } } public void consume(long howMuch) { try { super.consume(howMuch); } catch (ConnectionException e) { // It's a bug if we consume more than remote allowed throw new SSHRuntimeException(e); } } } /** Controls how much data remote end can send before an adjustment notification from us is required. */ public static final class Local extends Window { private final long initialSize; private final long threshold; public Local(long initialWinSize, int maxPacketSize, LoggerFactory loggerFactory) { super(initialWinSize, maxPacketSize, loggerFactory); this.initialSize = initialWinSize; threshold = Math.min(maxPacketSize * 20, initialSize / 4); } public long neededAdjustment() { synchronized (lock) { return (size <= threshold) ? (initialSize - size) : 0; } } } }