private Stream createLocalStream(Request request) { int streamId = nextLocalStreamId.getAndAdd(2); Integer key = Integer.valueOf(streamId); Stream result = new Stream(key, this, request); streams.put(key, result); return result; }
final void rePrioritise(AbstractStream parent, boolean exclusive, int weight) { if (log.isDebugEnabled()) { log.debug(sm.getString("stream.reprioritisation.debug", getConnectionId(), getIdentifier(), Boolean.toString(exclusive), parent.getIdentifier(), Integer.toString(weight))); } // Check if new parent is a descendant of this stream if (isDescendant(parent)) { parent.detachFromParent(); // Cast is always safe since any descendant of this stream must be // an instance of Stream getParentStream().addChild((Stream) parent); } if (exclusive) { // Need to move children of the new parent to be children of this // stream. Slightly convoluted to avoid concurrent modification. Iterator<Stream> parentsChildren = parent.getChildStreams().iterator(); while (parentsChildren.hasNext()) { Stream parentsChild = parentsChildren.next(); parentsChildren.remove(); this.addChild(parentsChild); } } detachFromParent(); parent.addChild(this); this.weight = weight; }
final synchronized int reserveWindowSize(int reservation, boolean block) throws IOException { long windowSize = getWindowSize(); while (windowSize < 1) { if (!canWrite()) { throw new CloseNowException(sm.getString("stream.notWritable", getConnectionId(), getIdentifier())); windowSize = getWindowSize(); allocation = reservation; decrementWindowSize(allocation); return allocation;
@Override public ByteBuffer startRequestBodyFrame(int streamId, int payloadSize) throws Http2Exception { Stream stream = getStream(streamId, true); stream.checkState(FrameType.DATA); stream.receivedData(payloadSize); return stream.getInputByteBuffer(); }
@Override public void reset(int streamId, long errorCode) throws Http2Exception { Stream stream = getStream(streamId, true); stream.checkState(FrameType.RST); stream.receiveReset(errorCode); }
private void removeStreamFromPriorityTree(Integer streamIdToRemove) { Stream streamToRemove = streams.remove(streamIdToRemove); // Move the removed Stream's children to the removed Stream's // parent. Set<Stream> children = streamToRemove.getChildStreams(); if (streamToRemove.getChildStreams().size() == 1) { // Shortcut streamToRemove.getChildStreams().iterator().next().rePrioritise( streamToRemove.getParentStream(), streamToRemove.getWeight()); } else { int totalWeight = 0; for (Stream child : children) { totalWeight += child.getWeight(); } for (Stream child : children) { streamToRemove.getChildStreams().iterator().next().rePrioritise( streamToRemove.getParentStream(), streamToRemove.getWeight() * child.getWeight() / totalWeight); } } streamToRemove.detachFromParent(); streamToRemove.getChildStreams().clear(); }
sendfile.streamReservation = sendfile.stream.reserveWindowSize(reservation, true); sendfile.connectionReservation = reserveWindowSize(sendfile.stream, sendfile.streamReservation, true); } catch (IOException e) { boolean finished = (frameSize == sendfile.left) && sendfile.stream.getCoyoteResponse().getTrailerFields() == null; boolean writeable = sendfile.stream.canWrite(); byte[] header = new byte[9]; ByteUtil.setThreeBytes(header, 0, frameSize); if (finished) { header[4] = FLAG_END_OF_STREAM; sendfile.stream.sentEndOfStream(); if (!sendfile.stream.isActive()) { activeRemoteStreamCount.decrementAndGet(); ByteUtil.set31Bits(header, 5, sendfile.stream.getIdentifier().intValue()); sendfile.mappedBuffer.limit(sendfile.mappedBuffer.position() + frameSize); socketWrapper.write(BlockingMode.SEMI_BLOCK, protocol.getWriteTimeout(),
@Override void writeBody(Stream stream, ByteBuffer data, int len, boolean finished) throws IOException { if (log.isDebugEnabled()) { log.debug(sm.getString("upgradeHandler.writeBody", connectionId, stream.getIdentifier(), Integer.toString(len))); } // Need to check this now since sending end of stream will change this. boolean writeable = stream.canWrite(); byte[] header = new byte[9]; ByteUtil.setThreeBytes(header, 0, len); header[3] = FrameType.DATA.getIdByte(); if (finished) { header[4] = FLAG_END_OF_STREAM; stream.sentEndOfStream(); if (!stream.isActive()) { activeRemoteStreamCount.decrementAndGet(); } } if (writeable) { ByteUtil.set31Bits(header, 5, stream.getIdentifier().intValue()); int orgLimit = data.limit(); data.limit(data.position() + len); socketWrapper.write(BlockingMode.BLOCK, protocol.getWriteTimeout(), TimeUnit.MILLISECONDS, null, SocketWrapperBase.COMPLETE_WRITE, applicationErrorCompletion, ByteBuffer.wrap(header), data); data.limit(orgLimit); handleAsyncException(); } }
Stream stream = entry.getValue(); if (stream.isActive()) { continue; if (stream.isClosedFinal()) { } else if (stream.getChildStreams().size() == 0) { removedStream.detachFromParent(); toClose--; if (log.isDebugEnabled()) { AbstractStream parent = removedStream.getParentStream(); while (parent instanceof Stream && !((Stream) parent).isActive() && !((Stream) parent).isClosedFinal() && parent.getChildStreams().size() == 0) { streams.remove(parent.getIdentifier()); parent.detachFromParent();
@Override protected final boolean flushBufferedWrite() throws IOException { if (log.isDebugEnabled()) { log.debug(sm.getString("streamProcessor.flushBufferedWrite.entry", stream.getConnectionId(), stream.getIdentifier())); } if (stream.flush(false)) { // The buffer wasn't fully flushed so re-register the // stream for write. Note this does not go via the // Response since the write registration state at // that level should remain unchanged. Once the buffer // has been emptied then the code below will call // dispatch() which will enable the // Response to respond to this event. if (stream.isReady()) { // Unexpected throw new IllegalStateException(); } return true; } return false; }
do { synchronized (this) { if (!stream.canWrite()) { throw new CloseNowException( sm.getString("upgradeHandler.stream.notWritable", stream.getConnectionId(), stream.getIdentifier())); backLogSize += reservation; AbstractStream parent = stream.getParentStream(); while (parent != null && backLogStreams.putIfAbsent(parent, new int[2]) == null) { parent = parent.getParentStream(); throw new IOException(sm.getString( "upgradeHandler.windowSizeReservationInterrupted", connectionId, stream.getIdentifier(), Integer.toString(reservation)), e);
@Override public void setting(Setting setting, long value) throws ConnectionException { // Special handling required if (setting == Setting.INITIAL_WINDOW_SIZE) { long oldValue = remoteSettings.getInitialWindowSize(); // Do this first in case new value is invalid remoteSettings.set(setting, value); int diff = (int) (value - oldValue); for (Stream stream : streams.values()) { try { stream.incrementWindowSize(diff); } catch (Http2Exception h2e) { stream.close(new StreamException(sm.getString( "upgradeHandler.windowSizeTooBig", connectionId, stream.getIdentifier()), h2e.getError(), stream.getIdentifier().intValue())); } } } else { remoteSettings.set(setting, value); } }
@Override void writeWindowUpdate(Stream stream, int increment, boolean applicationInitiated) throws IOException { if (!stream.canWrite()) { return; } // Build window update frame for stream 0 byte[] frame = new byte[13]; ByteUtil.setThreeBytes(frame, 0, 4); frame[3] = FrameType.WINDOW_UPDATE.getIdByte(); ByteUtil.set31Bits(frame, 9, increment); // Change stream Id byte[] frame2 = new byte[13]; ByteUtil.setThreeBytes(frame2, 0, 4); frame2[3] = FrameType.WINDOW_UPDATE.getIdByte(); ByteUtil.set31Bits(frame2, 9, increment); ByteUtil.set31Bits(frame2, 5, stream.getIdentifier().intValue()); socketWrapper.write(BlockingMode.SEMI_BLOCK, protocol.getWriteTimeout(), TimeUnit.MILLISECONDS, null, SocketWrapperBase.COMPLETE_WRITE, errorCompletion, ByteBuffer.wrap(frame), ByteBuffer.wrap(frame2)); handleAsyncException(); }
if (!getErrorState().isConnectionIoAllowed()) { ConnectionException ce = new ConnectionException(sm.getString( "streamProcessor.error.connection", stream.getConnectionId(), stream.getIdentifier()), Http2Error.INTERNAL_ERROR); stream.close(ce); } else if (!getErrorState().isIoAllowed()) { StreamException se = new StreamException(sm.getString( "streamProcessor.error.stream", stream.getConnectionId(), stream.getIdentifier()), Http2Error.INTERNAL_ERROR, stream.getIdentifier().intValue()); stream.close(se); stream.getConnectionId(), stream.getIdentifier()); if (log.isDebugEnabled()) { log.debug(msg, e); stream.close(ce); } finally { ContainerThreadMarker.clear();
@Override public void incrementWindowSize(int streamId, int increment) throws Http2Exception { if (streamId == 0) { incrementWindowSize(increment); } else { Stream stream = getStream(streamId, true); stream.checkState(FrameType.WINDOW_UPDATE); stream.incrementWindowSize(increment); } }
@Override public void reprioritise(int streamId, int parentStreamId, boolean exclusive, int weight) throws Http2Exception { if (streamId == parentStreamId) { throw new ConnectionException(sm.getString("upgradeHandler.dependency.invalid", getConnectionId(), Integer.valueOf(streamId)), Http2Error.PROTOCOL_ERROR); } Stream stream = getStream(streamId, false); if (stream == null) { stream = createRemoteStream(streamId); } stream.checkState(FrameType.PRIORITY); AbstractStream parentStream = getStream(parentStreamId, false); if (parentStream == null) { parentStream = this; } stream.rePrioritise(parentStream, exclusive, weight); }
Http2Error.PROTOCOL_ERROR); stream.checkState(FrameType.HEADERS); stream.receivedStartOfHeaders(headersEndStream); closeIdleStreams(streamId); if (localSettings.getMaxConcurrentStreams() < activeRemoteStreamCount.incrementAndGet()) {
private void closeIdleStreams(int newMaxActiveRemoteStreamId) throws Http2Exception { for (int i = maxActiveRemoteStreamId + 2; i < newMaxActiveRemoteStreamId; i += 2) { Stream stream = getStream(i, false); if (stream != null) { stream.closeIfIdle(); } } maxActiveRemoteStreamId = newMaxActiveRemoteStreamId; }
static void prepareHeaders(Request coyoteRequest, Response coyoteResponse, boolean noSendfile, Http2Protocol protocol, Stream stream) { MimeHeaders headers = coyoteResponse.getMimeHeaders(); int statusCode = coyoteResponse.getStatus(); // Add the pseudo header for status headers.addValue(":status").setString(Integer.toString(statusCode)); // Check to see if a response body is present if (!(statusCode < 200 || statusCode == 205 || statusCode == 304)) { String contentType = coyoteResponse.getContentType(); if (contentType != null) { headers.setValue("content-type").setString(contentType); } String contentLanguage = coyoteResponse.getContentLanguage(); if (contentLanguage != null) { headers.setValue("content-language").setString(contentLanguage); } } // Add date header unless it is an informational response or the // application has already set one if (statusCode >= 200 && headers.getValue("date") == null) { headers.addValue("date").setString(FastHttpDateFormat.getCurrentDate()); } // Compression can't be used with sendfile if (noSendfile && protocol != null && protocol.useCompression(coyoteRequest, coyoteResponse)) { // Enable compression. Headers will have been set. Need to configure // output filter at this point. stream.addOutputFilter(new GzipOutputFilter()); } }
sendStreamReset(se); } else { stream.close(se);