Search in sources :

Example 1 with H2StreamResetException

use of org.apache.hc.core5.http2.H2StreamResetException in project httpcomponents-core by apache.

the class H2IntegrationTest method testPushRefused.

@Test
public void testPushRefused() throws Exception {
    final BlockingQueue<Exception> pushResultQueue = new LinkedBlockingDeque<>();
    final InetSocketAddress serverEndpoint = server.start();
    server.register("/hello", new Supplier<AsyncServerExchangeHandler>() {

        @Override
        public AsyncServerExchangeHandler get() {
            return new MessageExchangeHandler<Void>(new DiscardingEntityConsumer<>()) {

                @Override
                protected void handle(final Message<HttpRequest, Void> request, final AsyncServerRequestHandler.ResponseTrigger responseTrigger, final HttpContext context) throws IOException, HttpException {
                    responseTrigger.pushPromise(new BasicHttpRequest(Method.GET, createRequestURI(serverEndpoint, "/stuff")), context, new BasicPushProducer(AsyncEntityProducers.create("Pushing all sorts of stuff")) {

                        @Override
                        public void failed(final Exception cause) {
                            pushResultQueue.add(cause);
                            super.failed(cause);
                        }
                    });
                    responseTrigger.pushPromise(new BasicHttpRequest(Method.GET, createRequestURI(serverEndpoint, "/more-stuff")), context, new BasicPushProducer(new MultiLineEntityProducer("Pushing lots of stuff", 500)) {

                        @Override
                        public void failed(final Exception cause) {
                            pushResultQueue.add(cause);
                            super.failed(cause);
                        }
                    });
                    responseTrigger.submitResponse(new BasicResponseProducer(HttpStatus.SC_OK, AsyncEntityProducers.create("Hi there")), context);
                }
            };
        }
    });
    client.start(H2Config.custom().setPushEnabled(true).build());
    final Future<ClientSessionEndpoint> connectFuture = client.connect("localhost", serverEndpoint.getPort(), TIMEOUT);
    final ClientSessionEndpoint streamEndpoint = connectFuture.get();
    final Future<Message<HttpResponse, String>> future1 = streamEndpoint.execute(new BasicRequestProducer(Method.GET, createRequestURI(serverEndpoint, "/hello")), new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null);
    final Message<HttpResponse, String> result1 = future1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
    Assertions.assertNotNull(result1);
    final HttpResponse response1 = result1.getHead();
    final String entity1 = result1.getBody();
    Assertions.assertNotNull(response1);
    Assertions.assertEquals(200, response1.getCode());
    Assertions.assertEquals("Hi there", entity1);
    final Object result2 = pushResultQueue.poll(5, TimeUnit.SECONDS);
    Assertions.assertNotNull(result2);
    Assertions.assertTrue(result2 instanceof H2StreamResetException);
    Assertions.assertEquals(H2Error.REFUSED_STREAM.getCode(), ((H2StreamResetException) result2).getCode());
    final Object result3 = pushResultQueue.poll(5, TimeUnit.SECONDS);
    Assertions.assertNotNull(result3);
    Assertions.assertTrue(result3 instanceof H2StreamResetException);
    Assertions.assertEquals(H2Error.REFUSED_STREAM.getCode(), ((H2StreamResetException) result3).getCode());
}
Also used : LinkedBlockingDeque(java.util.concurrent.LinkedBlockingDeque) Message(org.apache.hc.core5.http.Message) InetSocketAddress(java.net.InetSocketAddress) H2StreamResetException(org.apache.hc.core5.http2.H2StreamResetException) BasicHttpRequest(org.apache.hc.core5.http.message.BasicHttpRequest) HttpException(org.apache.hc.core5.http.HttpException) AsyncServerExchangeHandler(org.apache.hc.core5.http.nio.AsyncServerExchangeHandler) BasicHttpRequest(org.apache.hc.core5.http.message.BasicHttpRequest) HttpRequest(org.apache.hc.core5.http.HttpRequest) StringAsyncEntityConsumer(org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer) BasicPushProducer(org.apache.hc.core5.http.nio.support.BasicPushProducer) BasicRequestProducer(org.apache.hc.core5.http.nio.support.BasicRequestProducer) HttpContext(org.apache.hc.core5.http.protocol.HttpContext) HttpResponse(org.apache.hc.core5.http.HttpResponse) InterruptedIOException(java.io.InterruptedIOException) IOException(java.io.IOException) InterruptedIOException(java.io.InterruptedIOException) IOException(java.io.IOException) ExecutionException(java.util.concurrent.ExecutionException) URISyntaxException(java.net.URISyntaxException) H2StreamResetException(org.apache.hc.core5.http2.H2StreamResetException) HttpException(org.apache.hc.core5.http.HttpException) ProtocolException(org.apache.hc.core5.http.ProtocolException) DiscardingEntityConsumer(org.apache.hc.core5.http.nio.entity.DiscardingEntityConsumer) AsyncServerRequestHandler(org.apache.hc.core5.http.nio.AsyncServerRequestHandler) BasicResponseProducer(org.apache.hc.core5.http.nio.support.BasicResponseProducer) Test(org.junit.Test)

Example 2 with H2StreamResetException

use of org.apache.hc.core5.http2.H2StreamResetException in project httpcomponents-core by apache.

the class AbstractH2StreamMultiplexer method onTimeout.

public final void onTimeout(final Timeout timeout) throws HttpException, IOException {
    connState = ConnectionHandshake.SHUTDOWN;
    final RawFrame goAway;
    if (localSettingState != SettingsHandshake.ACKED) {
        goAway = frameFactory.createGoAway(processedRemoteStreamId, H2Error.SETTINGS_TIMEOUT, "Setting timeout (" + timeout + ")");
    } else {
        goAway = frameFactory.createGoAway(processedRemoteStreamId, H2Error.NO_ERROR, "Timeout due to inactivity (" + timeout + ")");
    }
    commitFrame(goAway);
    for (final Iterator<Map.Entry<Integer, H2Stream>> it = streamMap.entrySet().iterator(); it.hasNext(); ) {
        final Map.Entry<Integer, H2Stream> entry = it.next();
        final H2Stream stream = entry.getValue();
        stream.reset(new H2StreamResetException(H2Error.NO_ERROR, "Timeout due to inactivity (" + timeout + ")"));
    }
    streamMap.clear();
}
Also used : AtomicInteger(java.util.concurrent.atomic.AtomicInteger) RawFrame(org.apache.hc.core5.http2.frame.RawFrame) H2StreamResetException(org.apache.hc.core5.http2.H2StreamResetException) Map(java.util.Map) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap)

Example 3 with H2StreamResetException

use of org.apache.hc.core5.http2.H2StreamResetException in project httpcomponents-core by apache.

the class AbstractH2StreamMultiplexer method consumeFrame.

private void consumeFrame(final RawFrame frame) throws HttpException, IOException {
    final FrameType frameType = FrameType.valueOf(frame.getType());
    final int streamId = frame.getStreamId();
    if (continuation != null && frameType != FrameType.CONTINUATION) {
        throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "CONTINUATION frame expected");
    }
    switch(frameType) {
        case DATA:
            {
                final H2Stream stream = getValidStream(streamId);
                try {
                    consumeDataFrame(frame, stream);
                } catch (final H2StreamResetException ex) {
                    stream.localReset(ex);
                } catch (final HttpStreamResetException ex) {
                    stream.localReset(ex, ex.getCause() != null ? H2Error.INTERNAL_ERROR : H2Error.CANCEL);
                }
                if (stream.isTerminated()) {
                    streamMap.remove(streamId);
                    stream.releaseResources();
                    requestSessionOutput();
                }
            }
            break;
        case HEADERS:
            {
                if (streamId == 0) {
                    throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Illegal stream id: " + streamId);
                }
                H2Stream stream = streamMap.get(streamId);
                if (stream == null) {
                    acceptHeaderFrame();
                    if (idGenerator.isSameSide(streamId)) {
                        throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Illegal stream id: " + streamId);
                    }
                    if (goAwayReceived) {
                        throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "GOAWAY received");
                    }
                    updateLastStreamId(streamId);
                    final H2StreamChannelImpl channel = new H2StreamChannelImpl(streamId, false, initInputWinSize, initOutputWinSize);
                    final H2StreamHandler streamHandler;
                    if (connState.compareTo(ConnectionHandshake.ACTIVE) <= 0) {
                        streamHandler = createRemotelyInitiatedStream(channel, httpProcessor, connMetrics, null);
                    } else {
                        streamHandler = NoopH2StreamHandler.INSTANCE;
                        channel.setLocalEndStream();
                    }
                    stream = new H2Stream(channel, streamHandler, true);
                    if (stream.isOutputReady()) {
                        stream.produceOutput();
                    }
                    streamMap.put(streamId, stream);
                }
                try {
                    consumeHeaderFrame(frame, stream);
                    if (stream.isOutputReady()) {
                        stream.produceOutput();
                    }
                } catch (final H2StreamResetException ex) {
                    stream.localReset(ex);
                } catch (final HttpStreamResetException ex) {
                    stream.localReset(ex, ex.getCause() != null ? H2Error.INTERNAL_ERROR : H2Error.CANCEL);
                } catch (final HttpException ex) {
                    stream.handle(ex);
                }
                if (stream.isTerminated()) {
                    streamMap.remove(streamId);
                    stream.releaseResources();
                    requestSessionOutput();
                }
            }
            break;
        case CONTINUATION:
            {
                if (continuation == null) {
                    throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Unexpected CONTINUATION frame");
                }
                if (streamId != continuation.streamId) {
                    throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Unexpected CONTINUATION stream id: " + streamId);
                }
                final H2Stream stream = getValidStream(streamId);
                try {
                    consumeContinuationFrame(frame, stream);
                } catch (final H2StreamResetException ex) {
                    stream.localReset(ex);
                } catch (final HttpStreamResetException ex) {
                    stream.localReset(ex, ex.getCause() != null ? H2Error.INTERNAL_ERROR : H2Error.CANCEL);
                }
                if (stream.isTerminated()) {
                    streamMap.remove(streamId);
                    stream.releaseResources();
                    requestSessionOutput();
                }
            }
            break;
        case WINDOW_UPDATE:
            {
                final ByteBuffer payload = frame.getPayload();
                if (payload == null || payload.remaining() != 4) {
                    throw new H2ConnectionException(H2Error.FRAME_SIZE_ERROR, "Invalid WINDOW_UPDATE frame payload");
                }
                final int delta = payload.getInt();
                if (delta <= 0) {
                    throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Invalid WINDOW_UPDATE delta");
                }
                if (streamId == 0) {
                    try {
                        updateOutputWindow(0, connOutputWindow, delta);
                    } catch (final ArithmeticException ex) {
                        throw new H2ConnectionException(H2Error.FLOW_CONTROL_ERROR, ex.getMessage());
                    }
                } else {
                    final H2Stream stream = streamMap.get(streamId);
                    if (stream != null) {
                        try {
                            updateOutputWindow(streamId, stream.getOutputWindow(), delta);
                        } catch (final ArithmeticException ex) {
                            throw new H2ConnectionException(H2Error.FLOW_CONTROL_ERROR, ex.getMessage());
                        }
                    }
                }
                ioSession.setEvent(SelectionKey.OP_WRITE);
            }
            break;
        case RST_STREAM:
            {
                if (streamId == 0) {
                    throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Illegal stream id: " + streamId);
                }
                final H2Stream stream = streamMap.get(streamId);
                if (stream == null) {
                    if (streamId > lastStreamId.get()) {
                        throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Unexpected stream id: " + streamId);
                    }
                } else {
                    final ByteBuffer payload = frame.getPayload();
                    if (payload == null || payload.remaining() != 4) {
                        throw new H2ConnectionException(H2Error.FRAME_SIZE_ERROR, "Invalid RST_STREAM frame payload");
                    }
                    final int errorCode = payload.getInt();
                    stream.reset(new H2StreamResetException(errorCode, "Stream reset (" + errorCode + ")"));
                    streamMap.remove(streamId);
                    stream.releaseResources();
                    requestSessionOutput();
                }
            }
            break;
        case PING:
            {
                if (streamId != 0) {
                    throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Illegal stream id");
                }
                final ByteBuffer ping = frame.getPayloadContent();
                if (ping == null || ping.remaining() != 8) {
                    throw new H2ConnectionException(H2Error.FRAME_SIZE_ERROR, "Invalid PING frame payload");
                }
                if (frame.isFlagSet(FrameFlag.ACK)) {
                    final AsyncPingHandler pingHandler = pingHandlers.poll();
                    if (pingHandler != null) {
                        pingHandler.consumeResponse(ping);
                    }
                } else {
                    final ByteBuffer pong = ByteBuffer.allocate(ping.remaining());
                    pong.put(ping);
                    pong.flip();
                    final RawFrame response = frameFactory.createPingAck(pong);
                    commitFrame(response);
                }
            }
            break;
        case SETTINGS:
            {
                if (streamId != 0) {
                    throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Illegal stream id");
                }
                if (frame.isFlagSet(FrameFlag.ACK)) {
                    if (localSettingState == SettingsHandshake.TRANSMITTED) {
                        localSettingState = SettingsHandshake.ACKED;
                        ioSession.setEvent(SelectionKey.OP_WRITE);
                        applyLocalSettings();
                    }
                } else {
                    final ByteBuffer payload = frame.getPayload();
                    if (payload != null) {
                        if ((payload.remaining() % 6) != 0) {
                            throw new H2ConnectionException(H2Error.FRAME_SIZE_ERROR, "Invalid SETTINGS payload");
                        }
                        consumeSettingsFrame(payload);
                        remoteSettingState = SettingsHandshake.TRANSMITTED;
                    }
                    // Send ACK
                    final RawFrame response = frameFactory.createSettingsAck();
                    commitFrame(response);
                    remoteSettingState = SettingsHandshake.ACKED;
                }
            }
            break;
        case PRIORITY:
            // Stream priority not supported
            break;
        case PUSH_PROMISE:
            {
                acceptPushFrame();
                if (goAwayReceived) {
                    throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "GOAWAY received");
                }
                if (!localConfig.isPushEnabled()) {
                    throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Push is disabled");
                }
                final H2Stream stream = getValidStream(streamId);
                if (stream.isRemoteClosed()) {
                    stream.localReset(new H2StreamResetException(H2Error.STREAM_CLOSED, "Stream closed"));
                    break;
                }
                final ByteBuffer payload = frame.getPayloadContent();
                if (payload == null || payload.remaining() < 4) {
                    throw new H2ConnectionException(H2Error.FRAME_SIZE_ERROR, "Invalid PUSH_PROMISE payload");
                }
                final int promisedStreamId = payload.getInt();
                if (promisedStreamId == 0 || idGenerator.isSameSide(promisedStreamId)) {
                    throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Illegal promised stream id: " + promisedStreamId);
                }
                if (streamMap.get(promisedStreamId) != null) {
                    throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Unexpected promised stream id: " + promisedStreamId);
                }
                updateLastStreamId(promisedStreamId);
                final H2StreamChannelImpl channel = new H2StreamChannelImpl(promisedStreamId, false, initInputWinSize, initOutputWinSize);
                final H2StreamHandler streamHandler;
                if (connState.compareTo(ConnectionHandshake.ACTIVE) <= 0) {
                    streamHandler = createRemotelyInitiatedStream(channel, httpProcessor, connMetrics, stream.getPushHandlerFactory());
                } else {
                    streamHandler = NoopH2StreamHandler.INSTANCE;
                    channel.setLocalEndStream();
                }
                final H2Stream promisedStream = new H2Stream(channel, streamHandler, true);
                streamMap.put(promisedStreamId, promisedStream);
                try {
                    consumePushPromiseFrame(frame, payload, promisedStream);
                } catch (final H2StreamResetException ex) {
                    promisedStream.localReset(ex);
                } catch (final HttpStreamResetException ex) {
                    promisedStream.localReset(ex, ex.getCause() != null ? H2Error.INTERNAL_ERROR : H2Error.NO_ERROR);
                }
            }
            break;
        case GOAWAY:
            {
                if (streamId != 0) {
                    throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Illegal stream id");
                }
                final ByteBuffer payload = frame.getPayload();
                if (payload == null || payload.remaining() < 8) {
                    throw new H2ConnectionException(H2Error.FRAME_SIZE_ERROR, "Invalid GOAWAY payload");
                }
                final int processedLocalStreamId = payload.getInt();
                final int errorCode = payload.getInt();
                goAwayReceived = true;
                if (errorCode == H2Error.NO_ERROR.getCode()) {
                    if (connState.compareTo(ConnectionHandshake.ACTIVE) <= 0) {
                        for (final Iterator<Map.Entry<Integer, H2Stream>> it = streamMap.entrySet().iterator(); it.hasNext(); ) {
                            final Map.Entry<Integer, H2Stream> entry = it.next();
                            final int activeStreamId = entry.getKey();
                            if (!idGenerator.isSameSide(activeStreamId) && activeStreamId > processedLocalStreamId) {
                                final H2Stream stream = entry.getValue();
                                stream.cancel();
                                it.remove();
                            }
                        }
                    }
                    connState = streamMap.isEmpty() ? ConnectionHandshake.SHUTDOWN : ConnectionHandshake.GRACEFUL_SHUTDOWN;
                } else {
                    for (final Iterator<Map.Entry<Integer, H2Stream>> it = streamMap.entrySet().iterator(); it.hasNext(); ) {
                        final Map.Entry<Integer, H2Stream> entry = it.next();
                        final H2Stream stream = entry.getValue();
                        stream.reset(new H2StreamResetException(errorCode, "Connection terminated by the peer (" + errorCode + ")"));
                    }
                    streamMap.clear();
                    connState = ConnectionHandshake.SHUTDOWN;
                }
            }
            ioSession.setEvent(SelectionKey.OP_WRITE);
            break;
    }
}
Also used : H2ConnectionException(org.apache.hc.core5.http2.H2ConnectionException) H2StreamResetException(org.apache.hc.core5.http2.H2StreamResetException) ByteBuffer(java.nio.ByteBuffer) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) AsyncPingHandler(org.apache.hc.core5.http2.nio.AsyncPingHandler) FrameType(org.apache.hc.core5.http2.frame.FrameType) RawFrame(org.apache.hc.core5.http2.frame.RawFrame) Iterator(java.util.Iterator) HttpException(org.apache.hc.core5.http.HttpException) HttpStreamResetException(org.apache.hc.core5.http.HttpStreamResetException) Map(java.util.Map) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap)

Example 4 with H2StreamResetException

use of org.apache.hc.core5.http2.H2StreamResetException in project httpcomponents-core by apache.

the class AbstractH2StreamMultiplexer method consumeContinuationFrame.

private void consumeContinuationFrame(final RawFrame frame, final H2Stream stream) throws HttpException, IOException {
    final int streamId = frame.getStreamId();
    final ByteBuffer payload = frame.getPayload();
    continuation.copyPayload(payload);
    if (frame.isFlagSet(FrameFlag.END_HEADERS)) {
        final List<Header> headers = decodeHeaders(continuation.getContent());
        if (stream.isRemoteInitiated() && streamId > processedRemoteStreamId) {
            processedRemoteStreamId = streamId;
        }
        if (streamListener != null) {
            streamListener.onHeaderInput(this, streamId, headers);
        }
        if (stream.isRemoteClosed()) {
            throw new H2StreamResetException(H2Error.STREAM_CLOSED, "Stream already closed");
        }
        if (stream.isLocalReset()) {
            return;
        }
        if (continuation.endStream) {
            stream.setRemoteEndStream();
        }
        if (continuation.type == FrameType.PUSH_PROMISE.getValue()) {
            stream.consumePromise(headers);
        } else {
            stream.consumeHeader(headers);
        }
        continuation = null;
    }
}
Also used : Header(org.apache.hc.core5.http.Header) H2StreamResetException(org.apache.hc.core5.http2.H2StreamResetException) ByteBuffer(java.nio.ByteBuffer)

Example 5 with H2StreamResetException

use of org.apache.hc.core5.http2.H2StreamResetException in project httpcomponents-core by apache.

the class AbstractH2StreamMultiplexer method consumeHeaderFrame.

private void consumeHeaderFrame(final RawFrame frame, final H2Stream stream) throws HttpException, IOException {
    final int streamId = stream.getId();
    if (!frame.isFlagSet(FrameFlag.END_HEADERS)) {
        continuation = new Continuation(streamId, frame.getType(), frame.isFlagSet(FrameFlag.END_STREAM));
    }
    final ByteBuffer payload = frame.getPayloadContent();
    if (frame.isFlagSet(FrameFlag.PRIORITY)) {
        // Priority not supported
        payload.getInt();
        payload.get();
    }
    if (continuation == null) {
        final List<Header> headers = decodeHeaders(payload);
        if (stream.isRemoteInitiated() && streamId > processedRemoteStreamId) {
            processedRemoteStreamId = streamId;
        }
        if (streamListener != null) {
            streamListener.onHeaderInput(this, streamId, headers);
        }
        if (stream.isRemoteClosed()) {
            throw new H2StreamResetException(H2Error.STREAM_CLOSED, "Stream already closed");
        }
        if (stream.isLocalReset()) {
            return;
        }
        if (frame.isFlagSet(FrameFlag.END_STREAM)) {
            stream.setRemoteEndStream();
        }
        stream.consumeHeader(headers);
    } else {
        continuation.copyPayload(payload);
    }
}
Also used : Header(org.apache.hc.core5.http.Header) H2StreamResetException(org.apache.hc.core5.http2.H2StreamResetException) ByteBuffer(java.nio.ByteBuffer)

Aggregations

H2StreamResetException (org.apache.hc.core5.http2.H2StreamResetException)8 ByteBuffer (java.nio.ByteBuffer)4 HttpException (org.apache.hc.core5.http.HttpException)3 ProtocolException (org.apache.hc.core5.http.ProtocolException)3 Map (java.util.Map)2 ConcurrentHashMap (java.util.concurrent.ConcurrentHashMap)2 AtomicInteger (java.util.concurrent.atomic.AtomicInteger)2 Header (org.apache.hc.core5.http.Header)2 HttpRequest (org.apache.hc.core5.http.HttpRequest)2 AsyncServerExchangeHandler (org.apache.hc.core5.http.nio.AsyncServerExchangeHandler)2 BasicResponseProducer (org.apache.hc.core5.http.nio.support.BasicResponseProducer)2 H2ConnectionException (org.apache.hc.core5.http2.H2ConnectionException)2 RawFrame (org.apache.hc.core5.http2.frame.RawFrame)2 IOException (java.io.IOException)1 InterruptedIOException (java.io.InterruptedIOException)1 InetSocketAddress (java.net.InetSocketAddress)1 URISyntaxException (java.net.URISyntaxException)1 Iterator (java.util.Iterator)1 ExecutionException (java.util.concurrent.ExecutionException)1 LinkedBlockingDeque (java.util.concurrent.LinkedBlockingDeque)1