Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.arangodb.shaded.vertx.core.http.impl.Http2ConnectionBase Maven / Gradle / Ivy
/*
* Copyright (c) 2011-2019 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
* which is available at https://www.apache.org/licenses/LICENSE-2.0.
*
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
*/
package com.arangodb.shaded.vertx.core.http.impl;
import com.arangodb.shaded.netty.buffer.*;
import com.arangodb.shaded.netty.channel.ChannelFuture;
import com.arangodb.shaded.netty.channel.ChannelFutureListener;
import com.arangodb.shaded.netty.channel.ChannelHandlerContext;
import com.arangodb.shaded.netty.channel.ChannelPromise;
import com.arangodb.shaded.netty.handler.codec.http2.Http2Connection;
import com.arangodb.shaded.netty.handler.codec.http2.Http2Exception;
import com.arangodb.shaded.netty.handler.codec.http2.Http2Flags;
import com.arangodb.shaded.netty.handler.codec.http2.Http2FrameListener;
import com.arangodb.shaded.netty.handler.codec.http2.Http2Headers;
import com.arangodb.shaded.netty.handler.codec.http2.Http2Settings;
import com.arangodb.shaded.netty.handler.codec.http2.Http2Stream;
import com.arangodb.shaded.netty.handler.timeout.IdleStateEvent;
import com.arangodb.shaded.vertx.codegen.annotations.Nullable;
import com.arangodb.shaded.vertx.core.AsyncResult;
import com.arangodb.shaded.vertx.core.Future;
import com.arangodb.shaded.vertx.core.Handler;
import com.arangodb.shaded.vertx.core.Promise;
import com.arangodb.shaded.vertx.core.VertxException;
import com.arangodb.shaded.vertx.core.buffer.Buffer;
import com.arangodb.shaded.vertx.core.buffer.impl.VertxByteBufAllocator;
import com.arangodb.shaded.vertx.core.http.GoAway;
import com.arangodb.shaded.vertx.core.http.HttpClosedException;
import com.arangodb.shaded.vertx.core.http.HttpConnection;
import com.arangodb.shaded.vertx.core.http.StreamPriority;
import com.arangodb.shaded.vertx.core.impl.EventLoopContext;
import com.arangodb.shaded.vertx.core.impl.future.PromiseInternal;
import com.arangodb.shaded.vertx.core.impl.VertxInternal;
import com.arangodb.shaded.vertx.core.impl.logging.Logger;
import com.arangodb.shaded.vertx.core.impl.logging.LoggerFactory;
import com.arangodb.shaded.vertx.core.net.impl.ConnectionBase;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Map;
import java.util.Objects;
/**
* @author Julien Viet
*/
abstract class Http2ConnectionBase extends ConnectionBase implements Http2FrameListener, HttpConnection {
private static final Logger log = LoggerFactory.getLogger(Http2ConnectionBase.class);
private static ByteBuf safeBuffer(ByteBuf buf) {
ByteBuf buffer = VertxByteBufAllocator.DEFAULT.heapBuffer(buf.readableBytes());
buffer.writeBytes(buf);
return buffer;
}
protected final ChannelHandlerContext handlerContext;
protected final VertxHttp2ConnectionHandler handler;
protected final Http2Connection.PropertyKey streamKey;
private boolean shutdown;
private Handler remoteSettingsHandler;
private final ArrayDeque> updateSettingsHandlers = new ArrayDeque<>();
private final ArrayDeque> pongHandlers = new ArrayDeque<>();
private Http2Settings localSettings;
private Http2Settings remoteSettings;
private Handler goAwayHandler;
private Handler shutdownHandler;
private Handler pingHandler;
private GoAway goAwayStatus;
private int windowSize;
private long maxConcurrentStreams;
public Http2ConnectionBase(EventLoopContext context, VertxHttp2ConnectionHandler handler) {
super(context, handler.context());
this.handler = handler;
this.handlerContext = chctx;
this.windowSize = handler.connection().local().flowController().windowSize(handler.connection().connectionStream());
this.maxConcurrentStreams = io.vertx.core.http.Http2Settings.DEFAULT_MAX_CONCURRENT_STREAMS;
this.streamKey = handler.connection().newKey();
this.localSettings = handler.initialSettings();
}
VertxInternal vertx() {
return vertx;
}
@Override
public void handleClosed() {
super.handleClosed();
}
@Override
protected void handleInterestedOpsChanged() {
// Handled by HTTP/2
}
@Override
protected void handleIdle(IdleStateEvent event) {
super.handleIdle(event);
}
synchronized void onConnectionError(Throwable cause) {
ArrayList streams = new ArrayList<>();
try {
handler.connection().forEachActiveStream(stream -> {
streams.add(stream.getProperty(streamKey));
return true;
});
} catch (Http2Exception e) {
log.error("Could not get the list of active streams", e);
}
for (VertxHttp2Stream stream : streams) {
stream.context.dispatch(v -> stream.handleException(cause));
}
handleException(cause);
}
VertxHttp2Stream stream(int id) {
Http2Stream s = handler.connection().stream(id);
if (s == null) {
return null;
}
return s.getProperty(streamKey);
}
void onStreamError(int streamId, Throwable cause) {
VertxHttp2Stream stream = stream(streamId);
if (stream != null) {
stream.onException(cause);
}
}
void onStreamWritabilityChanged(Http2Stream s) {
VertxHttp2Stream stream = s.getProperty(streamKey);
if (stream != null) {
stream.onWritabilityChanged();
}
}
void onStreamClosed(Http2Stream s) {
VertxHttp2Stream stream = s.getProperty(streamKey);
if (stream != null) {
boolean active = chctx.channel().isActive();
if (goAwayStatus != null) {
stream.onException(new HttpClosedException(goAwayStatus));
} else if (!active) {
stream.onException(HttpUtils.STREAM_CLOSED_EXCEPTION);
}
stream.onClose();
}
checkShutdown();
}
boolean onGoAwaySent(GoAway goAway) {
synchronized (this) {
if (this.goAwayStatus != null) {
return false;
}
this.goAwayStatus = goAway;
}
checkShutdown();
return true;
}
boolean onGoAwayReceived(GoAway goAway) {
Handler handler;
synchronized (this) {
if (this.goAwayStatus != null) {
return false;
}
this.goAwayStatus = goAway;
handler = goAwayHandler;
}
if (handler != null) {
context.dispatch(new GoAway(goAway), handler);
}
checkShutdown();
return true;
}
// Http2FrameListener
@Override
public void onPriorityRead(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight, boolean exclusive) {
VertxHttp2Stream stream = stream(streamId);
if (stream != null) {
StreamPriority streamPriority = new StreamPriority()
.setDependency(streamDependency)
.setWeight(weight)
.setExclusive(exclusive);
stream.onPriorityChange(streamPriority);
}
}
@Override
public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency, short weight, boolean exclusive, int padding, boolean endOfStream) throws Http2Exception {
StreamPriority streamPriority = new StreamPriority()
.setDependency(streamDependency)
.setWeight(weight)
.setExclusive(exclusive);
onHeadersRead(streamId, headers, streamPriority, endOfStream);
}
@Override
public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding, boolean endOfStream) throws Http2Exception {
onHeadersRead(streamId, headers, null, endOfStream);
}
protected abstract void onHeadersRead(int streamId, Http2Headers headers, StreamPriority streamPriority, boolean endOfStream);
@Override
public void onSettingsAckRead(ChannelHandlerContext ctx) {
Handler handler;
synchronized (this) {
handler = updateSettingsHandlers.poll();
}
if (handler != null) {
// No need to run on a particular context it shall be done by the handler instead
context.emit(handler);
}
}
protected void concurrencyChanged(long concurrency) {
}
@Override
public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) {
boolean changed;
Handler handler;
synchronized (this) {
Long val = settings.maxConcurrentStreams();
if (val != null) {
if (remoteSettings != null) {
changed = val != maxConcurrentStreams;
} else {
changed = false;
}
maxConcurrentStreams = val;
} else {
changed = false;
}
remoteSettings = settings;
handler = remoteSettingsHandler;
}
if (handler != null) {
context.dispatch(HttpUtils.toVertxSettings(settings), handler);
}
if (changed) {
concurrencyChanged(maxConcurrentStreams);
}
}
@Override
public void onPingRead(ChannelHandlerContext ctx, long data) throws Http2Exception {
Handler handler = pingHandler;
if (handler != null) {
Buffer buff = Buffer.buffer().appendLong(data);
context.dispatch(v -> handler.handle(buff));
}
}
@Override
public void onPingAckRead(ChannelHandlerContext ctx, long data) {
Promise handler = pongHandlers.poll();
if (handler != null) {
Buffer buff = Buffer.buffer().appendLong(data);
handler.complete(buff);
}
}
@Override
public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
Http2Headers headers, int padding) throws Http2Exception {
}
@Override
public void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData) {
}
@Override
public void onWindowUpdateRead(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement) {
}
@Override
public void onUnknownFrame(ChannelHandlerContext ctx, byte frameType, int streamId,
Http2Flags flags, ByteBuf payload) {
VertxHttp2Stream stream = stream(streamId);
if (stream != null) {
Buffer buff = Buffer.buffer(safeBuffer(payload));
stream.onCustomFrame(new HttpFrameImpl(frameType, flags.value(), buff));
}
}
@Override
public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) {
VertxHttp2Stream stream = stream(streamId);
if (stream != null) {
stream.onReset(errorCode);
}
}
@Override
public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, boolean endOfStream) {
VertxHttp2Stream stream = stream(streamId);
if (stream != null) {
data = safeBuffer(data);
Buffer buff = Buffer.buffer(data);
stream.onData(buff);
if (endOfStream) {
stream.onEnd();
}
}
return padding;
}
@Override
public int getWindowSize() {
return windowSize;
}
@Override
public HttpConnection setWindowSize(int windowSize) {
try {
Http2Stream stream = handler.encoder().connection().connectionStream();
int delta = windowSize - this.windowSize;
handler.decoder().flowController().incrementWindowSize(stream, delta);
this.windowSize = windowSize;
return this;
} catch (Http2Exception e) {
throw new VertxException(e);
}
}
@Override
public HttpConnection goAway(long errorCode, int lastStreamId, Buffer debugData) {
if (errorCode < 0) {
throw new IllegalArgumentException();
}
if (lastStreamId < 0) {
lastStreamId = handler.connection().remote().lastStreamCreated();
}
handler.writeGoAway(errorCode, lastStreamId, debugData != null ? debugData.getByteBuf() : Unpooled.EMPTY_BUFFER);
return this;
}
@Override
public synchronized HttpConnection goAwayHandler(Handler handler) {
goAwayHandler = handler;
return this;
}
@Override
public synchronized HttpConnection shutdownHandler(Handler handler) {
shutdownHandler = handler;
return this;
}
@Override
public void shutdown(long timeout, Handler> handler) {
shutdown(timeout, vertx.promise(handler));
}
@Override
public Future shutdown(long timeoutMs) {
PromiseInternal promise = vertx.promise();
shutdown(timeoutMs, promise);
return promise.future();
}
private void shutdown(long timeout, PromiseInternal promise) {
if (timeout < 0) {
promise.fail("Invalid timeout value " + timeout);
return;
}
handler.gracefulShutdownTimeoutMillis(timeout);
ChannelFuture fut = channel().close();
fut.addListener(promise);
}
@Override
public Http2ConnectionBase closeHandler(Handler handler) {
return (Http2ConnectionBase) super.closeHandler(handler);
}
@Override
public Future close() {
PromiseInternal promise = context.promise();
ChannelPromise pr = chctx.newPromise();
ChannelPromise channelPromise = pr.addListener(promise);
handlerContext.writeAndFlush(Unpooled.EMPTY_BUFFER, pr);
channelPromise.addListener((ChannelFutureListener) future -> shutdown(0L));
return promise.future();
}
@Override
public synchronized HttpConnection remoteSettingsHandler(Handler handler) {
remoteSettingsHandler = handler;
return this;
}
@Override
public synchronized com.arangodb.shaded.vertx.core.http.Http2Settings remoteSettings() {
return HttpUtils.toVertxSettings(remoteSettings);
}
@Override
public synchronized com.arangodb.shaded.vertx.core.http.Http2Settings settings() {
return HttpUtils.toVertxSettings(localSettings);
}
@Override
public Future updateSettings(com.arangodb.shaded.vertx.core.http.Http2Settings settings) {
Promise promise = context.promise();
Http2Settings settingsUpdate = HttpUtils.fromVertxSettings(settings);
updateSettings(settingsUpdate, promise);
return promise.future();
}
@Override
public HttpConnection updateSettings(com.arangodb.shaded.vertx.core.http.Http2Settings settings, @Nullable Handler> completionHandler) {
updateSettings(settings).onComplete(completionHandler);
return this;
}
protected void updateSettings(Http2Settings settingsUpdate, Handler> completionHandler) {
Http2Settings current = handler.decoder().localSettings();
for (Map.Entry entry : current.entrySet()) {
Character key = entry.getKey();
if (Objects.equals(settingsUpdate.get(key), entry.getValue())) {
settingsUpdate.remove(key);
}
}
Handler pending = v -> {
synchronized (Http2ConnectionBase.this) {
localSettings.putAll(settingsUpdate);
}
if (completionHandler != null) {
completionHandler.handle(Future.succeededFuture());
}
};
updateSettingsHandlers.add(pending);
handler.writeSettings(settingsUpdate).addListener(fut -> {
if (!fut.isSuccess()) {
synchronized (Http2ConnectionBase.this) {
updateSettingsHandlers.remove(pending);
}
if (completionHandler != null) {
completionHandler.handle(Future.failedFuture(fut.cause()));
}
}
});
}
@Override
public Future ping(Buffer data) {
if (data.length() != 8) {
throw new IllegalArgumentException("Ping data must be exactly 8 bytes");
}
Promise promise = context.promise();
handler.writePing(data.getLong(0)).addListener(fut -> {
if (fut.isSuccess()) {
synchronized (Http2ConnectionBase.this) {
pongHandlers.add(promise);
}
} else {
promise.fail(fut.cause());
}
});
return promise.future();
}
@Override
public HttpConnection ping(Buffer data, Handler> pongHandler) {
Future fut = ping(data);
if (pongHandler != null) {
fut.onComplete(pongHandler);
}
return this;
}
@Override
public synchronized HttpConnection pingHandler(Handler handler) {
pingHandler = handler;
return this;
}
// Necessary to set the covariant return type
@Override
public Http2ConnectionBase exceptionHandler(Handler handler) {
return (Http2ConnectionBase) super.exceptionHandler(handler);
}
void consumeCredits(Http2Stream stream, int numBytes) {
this.handler.consume(stream, numBytes);
}
// Private
private void checkShutdown() {
Handler shutdownHandler;
synchronized (this) {
if (shutdown) {
return;
}
Http2Connection conn = handler.connection();
if ((!conn.goAwayReceived() && !conn.goAwaySent()) || conn.numActiveStreams() > 0) {
return;
}
shutdown = true;
shutdownHandler = this.shutdownHandler;
}
doShutdown(shutdownHandler);
}
protected void doShutdown(Handler shutdownHandler) {
if (shutdownHandler != null) {
context.dispatch(shutdownHandler);
}
}
}