public Memento prepareToParse() { MementoImpl memento = new MementoImpl(); memento.setLeftOverData(dataGen.emptyWrapper()); return memento; }
void incrementOutgoingWindow(int streamId, int length) { log.info("incrementing outgoing window for {} by {}", streamId, length); if(outgoingFlowControl.get(0x0).addAndGet(length) > 2147483647L) throw new GoAwayError(lastClosedRemoteOriginatedStream().orElse(0), Http2ErrorCode.FLOW_CONTROL_ERROR, wrapperGen.emptyWrapper()); if(outgoingFlowControl.get(streamId) == null) initializeFlowControl(streamId); if(outgoingFlowControl.get(streamId).addAndGet(length) > 2147483647L) throw new GoAwayError(lastClosedRemoteOriginatedStream().orElse(0), streamId, Http2ErrorCode.FLOW_CONTROL_ERROR, wrapperGen.emptyWrapper()); log.info("stream {} outgoing window is {} and connection window is {}", streamId, outgoingFlowControl.get(streamId), outgoingFlowControl.get(0)); }
@Override public CompletableFuture<DataWriter> incomingResponse(HttpResponse resp, boolean isComplete) { HttpFullResponse resp1 = new HttpFullResponse(resp, dataGen.emptyWrapper()); if(isComplete) { future.complete(resp1); return CompletableFuture.completedFuture(new NullWriter()); } response = resp1; return CompletableFuture.completedFuture(new DataWriterImpl()); }
void decrementIncomingWindow(int streamId, int length) { log.info("decrementing incoming window for {} by {}", streamId, length); if(incomingFlowControl.get(0x0).addAndGet(- length) < 0) { throw new GoAwayError(lastClosedRemoteOriginatedStream().orElse(0), Http2ErrorCode.FLOW_CONTROL_ERROR, wrapperGen.emptyWrapper()); } if(incomingFlowControl.get(streamId).addAndGet(- length) < 0) { throw new RstStreamError(Http2ErrorCode.FLOW_CONTROL_ERROR, streamId); } log.info("stream {} incoming window is {} and connection window is {}", streamId, incomingFlowControl.get(streamId), incomingFlowControl.get(0)); }
private ByteBuffer translate(List<Http2Msg> frames) { DataWrapper allData = dataGen.emptyWrapper(); for(Http2Msg f : frames) { DataWrapper data = this.http2EngineImpl.marshal(f); allData = dataGen.chainDataWrappers(allData, data); } byte[] byteArray = allData.createByteArray(); return ByteBuffer.wrap(byteArray); }
private void preconditions() { // If we haven't gotten the settings, let's wait a little bit because another thread // might have the settings frame and hasn't gotten around to processing it yet. try { Http2EngineImpl.log.info("Waiting for settings frame to arrive"); if (!this.http2EngineImpl.settingsLatch.await(500, TimeUnit.MILLISECONDS)) throw new GoAwayError(this.http2EngineImpl.lastClosedRemoteOriginatedStream().orElse(0), Http2ErrorCode.PROTOCOL_ERROR, Http2EngineImpl.wrapperGen.emptyWrapper()); } catch (InterruptedException e) { Http2EngineImpl.log.error("Caught exception while waiting for settings frame", e); throw new GoAwayError(this.http2EngineImpl.lastClosedRemoteOriginatedStream().orElse(0), Http2ErrorCode.PROTOCOL_ERROR, Http2EngineImpl.wrapperGen.emptyWrapper()); } }
public void uncompressBodyAndAssertContainsString(String text) { Header header = getResponse().getHeaderLookupStruct().getHeader(KnownHeaderName.CONTENT_ENCODING); if(header == null) throw new IllegalStateException("Body is not compressed as no CONTENT_ENCODING header field exists"); else if(!"gzip".equals(header.getValue())) throw new IllegalStateException("Body has wrong compression type="+header.getValue()+" in CONTENT_ENCODING header field"); DataWrapper wrapper = getBody(); byte[] compressed = wrapper.createByteArray(); ByteArrayInputStream in = new ByteArrayInputStream(compressed); byte[] out = new byte[10000]; DataWrapper output = dataGen.emptyWrapper(); try (GZIPInputStream str = new GZIPInputStream(in)) { int read = 0; while((read = str.read(out)) > 0) { ByteBuffer buffer = ByteBuffer.wrap(out, 0, read); DataWrapper byteWrapper = dataGen.wrapByteBuffer(buffer); output = dataGen.chainDataWrappers(output, byteWrapper); out = new byte[10000]; } } catch(IOException e) { throw new RuntimeException(e); } Charset charset = extractCharset(); String bodyAsString = output.createStringFrom(0, output.getReadableSize(), charset); if(!bodyAsString.contains(text)) throw new IllegalStateException("Expected compressed body to contain='"+text+"' but body was="+bodyAsString); }
private void handleRstStream(RstStreamFrame frame, Stream stream) { switch(stream.getStatus()) { case OPEN: case HALF_CLOSED_REMOTE: case HALF_CLOSED_LOCAL: case RESERVED_LOCAL: case RESERVED_REMOTE: case CLOSED: this.http2EngineImpl.sideSpecificHandleRstStream(frame, stream); stream.setStatus(CLOSED); break; default: throw new GoAwayError(this.http2EngineImpl.lastClosedRemoteOriginatedStream().orElse(0), Http2ErrorCode.PROTOCOL_ERROR, Http2EngineImpl.wrapperGen.emptyWrapper()); } }
void checkHeaders(Headers headerLookupStruct, Stream stream) { if(headerLookupStruct.getHeader(KnownHeaderName.CONTENT_LENGTH) != null) { stream.setContentLengthHeaderValue(Long.parseLong(headerLookupStruct.getHeader(KnownHeaderName.CONTENT_LENGTH).getValue())); } if(headerLookupStruct.getHeader(KnownHeaderName.CONNECTION) != null) { throw new GoAwayError(lastClosedRemoteOriginatedStream().orElse(0), stream.getStreamId(), Http2ErrorCode.PROTOCOL_ERROR, wrapperGen.emptyWrapper()); } if(headerLookupStruct.getHeader(KnownHeaderName.TE) != null && !headerLookupStruct.getHeader(KnownHeaderName.TE).getValue().toLowerCase().equals("trailers")) { throw new GoAwayError(lastClosedRemoteOriginatedStream().orElse(0), stream.getStreamId(), Http2ErrorCode.PROTOCOL_ERROR, wrapperGen.emptyWrapper()); } if(headerLookupStruct.getHeaders(TRAILER) != null) { for(Header header: headerLookupStruct.getHeaders(TRAILER)) { stream.addTrailerHeader(header.getValue().toLowerCase()); } } } }
private void handleWindowUpdate(WindowUpdateFrame frame, Stream stream) { if(frame.getWindowSizeIncrement() == 0) { throw new GoAwayError(this.http2EngineImpl.lastClosedRemoteOriginatedStream().orElse(0), Http2ErrorCode.PROTOCOL_ERROR, Http2EngineImpl.wrapperGen.emptyWrapper()); } this.http2EngineImpl.incrementOutgoingWindow(frame.getStreamId(), frame.getWindowSizeIncrement()); // clear all queues if the connection-level stream if(frame.getStreamId() == 0x0) { for (Map.Entry<Integer, ConcurrentLinkedDeque<PendingData>> entry : this.http2EngineImpl.outgoingDataQueue.entrySet()) { if (!entry.getValue().isEmpty()) this.http2EngineImpl.clearQueue(entry.getKey()); } } else { if(stream.getStatus() == IDLE) { throw new GoAwayError(this.http2EngineImpl.lastClosedRemoteOriginatedStream().orElse(0), Http2ErrorCode.PROTOCOL_ERROR, Http2EngineImpl.wrapperGen.emptyWrapper()); } if(this.http2EngineImpl.outgoingDataQueue.containsKey(frame.getStreamId())) { this.http2EngineImpl.clearQueue(frame.getStreamId()); } } }
private void handlePushPromise(Http2Push frame, Stream stream) { if(this.http2EngineImpl.side == SERVER) { // Can't get pushpromise in the server throw new GoAwayError(this.http2EngineImpl.lastClosedRemoteOriginatedStream().orElse(0), Http2ErrorCode.PROTOCOL_ERROR, Http2EngineImpl.wrapperGen.emptyWrapper()); } long currentlyOpenStreams = this.http2EngineImpl.countOpenRemoteOriginatedStreams(); Http2EngineImpl.log.info("got push promise with currently open streams: " + currentlyOpenStreams); if(this.http2EngineImpl.localSettings.containsKey(SETTINGS_MAX_CONCURRENT_STREAMS) && currentlyOpenStreams >= this.http2EngineImpl.localSettings.get(SETTINGS_MAX_CONCURRENT_STREAMS)) { throw new RstStreamError(Http2ErrorCode.REFUSED_STREAM, frame.getPromisedStreamId()); } int newStreamId = frame.getPromisedStreamId(); if(newStreamId <= this.http2EngineImpl.lastIncomingStreamId.get()) { throw new GoAwayError(this.http2EngineImpl.lastClosedRemoteOriginatedStream().orElse(0), Http2ErrorCode.PROTOCOL_ERROR, Http2EngineImpl.wrapperGen.emptyWrapper()); } this.http2EngineImpl.lastIncomingStreamId.set(newStreamId); Stream promisedStream = new Stream(); this.http2EngineImpl.initializeFlowControl(newStreamId); promisedStream.setStreamId(newStreamId); // TODO: make sure streamid is valid // TODO: close all lower numbered even IDLE streams this.http2EngineImpl.activeStreams.put(newStreamId, promisedStream); // Uses the same listener as the stream it came in on promisedStream.setResponseListener(stream.getResponseListener()); HttpRequest request = this.http2EngineImpl.requestFromHeaders(new LinkedList<>(frame.getHeaders()), promisedStream); promisedStream.setRequest(request); promisedStream.setStatus(Stream.StreamStatus.RESERVED_REMOTE); }
throw new GoAwayError(lastClosedRemoteOriginatedStream().orElse(0), Http2ErrorCode.PROTOCOL_ERROR, wrapperGen.emptyWrapper()); throw new GoAwayError(lastClosedRemoteOriginatedStream().orElse(0), Http2ErrorCode.FLOW_CONTROL_ERROR, wrapperGen.emptyWrapper()); throw new GoAwayError(lastClosedRemoteOriginatedStream().orElse(0), Http2ErrorCode.PROTOCOL_ERROR, wrapperGen.emptyWrapper());
throw new GoAwayError(0, Http2ErrorCode.PROTOCOL_ERROR, Http2EngineImpl.wrapperGen.emptyWrapper());
int streamId = frame.getStreamId(); if(streamId <= this.http2EngineImpl.lastIncomingStreamId.get() || frame.getStreamId() % 2 != 1) { throw new GoAwayError(this.http2EngineImpl.lastClosedRemoteOriginatedStream().orElse(0), Http2ErrorCode.PROTOCOL_ERROR, Http2EngineImpl.wrapperGen.emptyWrapper()); this.http2EngineImpl.activeStreams.put(stream.getStreamId(), stream); } else { throw new GoAwayError(this.http2EngineImpl.lastClosedRemoteOriginatedStream().orElse(0), Http2ErrorCode.PROTOCOL_ERROR, Http2EngineImpl.wrapperGen.emptyWrapper()); default: throw new GoAwayError(this.http2EngineImpl.lastClosedRemoteOriginatedStream().orElse(0), frame.getStreamId(), Http2ErrorCode.PROTOCOL_ERROR, Http2EngineImpl.wrapperGen.emptyWrapper()); default: throw new GoAwayError(this.http2EngineImpl.lastClosedRemoteOriginatedStream().orElse(0), Http2ErrorCode.PROTOCOL_ERROR, Http2EngineImpl.wrapperGen.emptyWrapper());
CompletableFuture<Void> sendHeaderFrames(LinkedList<Http2Header> headerList, Stream stream) { // TODO: check the status of the stream to ensure we can send HEADER frames ByteArrayOutputStream out = new ByteArrayOutputStream(); updateMaxHeaderTableSize(out); Http2Headers headers = new Http2Headers(headerList); headers.setStreamId(stream.getStreamId()); headers.setEndOfStream(false); DataWrapper data = marshal(headers); ByteBuffer buf = ByteBuffer.wrap(data.createByteArray()); // Send all the frames at once log.info("sending header frames: " + headers); return channel.write(buf).thenAccept( channel -> { switch (stream.getStatus()) { case IDLE: stream.setStatus(Stream.StreamStatus.OPEN); break; case HALF_CLOSED_LOCAL: case HALF_CLOSED_REMOTE: // leave status the same break; case RESERVED_LOCAL: stream.setStatus(HALF_CLOSED_REMOTE); break; default: throw new InternalError(lastClosedRemoteOriginatedStream().orElse(0), wrapperGen.emptyWrapper()); //"should not be sending headers on a stream not open or half closed remote" } } ); }
private void parseStuff(DataWrapper newData) { try { parseStuffTranslate(newData); for (Http2Msg frame : unmarshalState.getParsedFrames()) { Http2EngineImpl.log.info("got frame=" + frame); handleFrame(frame); } } catch (Http2Exception e) { if(e.getErrorType() == ErrorType.CONNECTION) { if(e.hasStream()) { throw new GoAwayError(this.http2EngineImpl.lastClosedRemoteOriginatedStream().orElse(0), e.getStreamId(), e.getReason().getErrorCode(), Http2EngineImpl.wrapperGen.emptyWrapper()); } else { throw new GoAwayError(this.http2EngineImpl.lastClosedRemoteOriginatedStream().orElse(0), e.getReason().getErrorCode(), Http2EngineImpl.wrapperGen.emptyWrapper()); } } else { throw new RstStreamError(e.getReason().getErrorCode(), e.getStreamId(), e); } } }
private void handleData(DataFrame frame, Stream stream) { // Only allowable if stream is open or half closed local switch(stream.getStatus()) { case OPEN: case HALF_CLOSED_LOCAL: int dataSize = frame.getData().getReadableSize(); int paddingSize = frame.getPadding().getReadableSize(); if(paddingSize > 0) { paddingSize += 1; //add the length byte in since there is padding } int payloadLength = dataSize + paddingSize; this.http2EngineImpl.decrementIncomingWindow(frame.getStreamId(), payloadLength); stream.checkAgainstContentLength(frame.getData().getReadableSize(), frame.isEndOfStream()); this.http2EngineImpl.sideSpecificHandleData(frame, payloadLength, stream); if(frame.isEndOfStream()) this.http2EngineImpl.receivedEndStream(stream); break; case HALF_CLOSED_REMOTE: throw new GoAwayError(this.http2EngineImpl.lastClosedRemoteOriginatedStream().orElse(0), stream.getStreamId(), Http2ErrorCode.STREAM_CLOSED, Http2EngineImpl.wrapperGen.emptyWrapper()); case CLOSED: throw new GoAwayError(this.http2EngineImpl.lastClosedRemoteOriginatedStream().orElse(0), stream.getStreamId(), Http2ErrorCode.STREAM_CLOSED, Http2EngineImpl.wrapperGen.emptyWrapper()); case IDLE: throw new GoAwayError(this.http2EngineImpl.lastClosedRemoteOriginatedStream().orElse(0), stream.getStreamId(), Http2ErrorCode.PROTOCOL_ERROR, Http2EngineImpl.wrapperGen.emptyWrapper()); default: throw new RstStreamError(Http2ErrorCode.PROTOCOL_ERROR, stream.getStreamId()); } }
case OPEN: if(!stream.isTrailerEnabled()) { throw new GoAwayError(this.http2EngineImpl.lastClosedRemoteOriginatedStream().orElse(0), stream.getStreamId(), Http2ErrorCode.STREAM_CLOSED, Http2EngineImpl.wrapperGen.emptyWrapper()); } else { isTrailer = true; throw new GoAwayError(this.http2EngineImpl.lastClosedRemoteOriginatedStream().orElse(0), stream.getStreamId(), Http2ErrorCode.STREAM_CLOSED, Http2EngineImpl.wrapperGen.emptyWrapper()); throw new GoAwayError(this.http2EngineImpl.lastClosedRemoteOriginatedStream().orElse(0), Http2ErrorCode.COMPRESSION_ERROR, Http2EngineImpl.wrapperGen.emptyWrapper());