HTTP transport implementation
[netconf.git] / transport / transport-http / src / main / java / org / opendaylight / netconf / transport / http / Http2Utils.java
diff --git a/transport/transport-http/src/main/java/org/opendaylight/netconf/transport/http/Http2Utils.java b/transport/transport-http/src/main/java/org/opendaylight/netconf/transport/http/Http2Utils.java
new file mode 100644 (file)
index 0000000..70979a8
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2024 PANTHEON.tech s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netconf.transport.http;
+
+import static io.netty.handler.codec.http2.HttpConversionUtil.ExtensionHeaderNames.STREAM_ID;
+
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.handler.codec.http.HttpMessage;
+import io.netty.handler.codec.http2.DefaultHttp2Connection;
+import io.netty.handler.codec.http2.DelegatingDecompressorFrameListener;
+import io.netty.handler.codec.http2.Http2ConnectionHandler;
+import io.netty.handler.codec.http2.Http2FrameLogger;
+import io.netty.handler.codec.http2.Http2Settings;
+import io.netty.handler.codec.http2.HttpToHttp2ConnectionHandlerBuilder;
+import io.netty.handler.codec.http2.InboundHttp2ToHttpAdapterBuilder;
+import io.netty.handler.logging.LogLevel;
+
+/**
+ * Collection of utility methods building HTTP/2 handlers.
+ */
+final class Http2Utils {
+
+    private static final Http2FrameLogger CLIENT_FRAME_LOGGER = new Http2FrameLogger(LogLevel.INFO, "Client");
+    private static final Http2FrameLogger SERVER_FRAME_LOGGER = new Http2FrameLogger(LogLevel.INFO, "Server");
+
+    private Http2Utils() {
+        // utility class
+    }
+
+    /**
+     * Build external HTTP/2 to internal Http 1.1. adaptor handler.
+     *
+     * @param server true for server, false for client
+     * @param maxContentLength max content length for http messages
+     * @return connection handler instance
+     */
+    static Http2ConnectionHandler connectionHandler(final boolean server, final int maxContentLength) {
+        final var connection = new DefaultHttp2Connection(server);
+        return new HttpToHttp2ConnectionHandlerBuilder()
+            .frameListener(new DelegatingDecompressorFrameListener(
+                connection,
+                new InboundHttp2ToHttpAdapterBuilder(connection)
+                    .maxContentLength(maxContentLength)
+                    .propagateSettings(true)
+                    .build()))
+            .connection(connection)
+            .frameLogger(server ? SERVER_FRAME_LOGGER : CLIENT_FRAME_LOGGER)
+            .gracefulShutdownTimeoutMillis(0L)
+            .build();
+    }
+
+    /**
+     * Build a handler consuming Http2Settings message.
+     *
+     * @return handler instance
+     */
+    static ChannelHandler clientSettingsHandler() {
+        return new SimpleChannelInboundHandler<Http2Settings>() {
+            @Override
+            protected void channelRead0(final ChannelHandlerContext ctx, final Http2Settings msg) throws Exception {
+                // the HTTP 2 Settings message is expected once, just consume it then remove itself
+                ctx.pipeline().remove(this);
+            }
+        };
+    }
+
+    /**
+     * Copies HTTP/2 associated stream id value (if exists) from one HTTP 1.1 message to another.
+     *
+     * @param from the message object to copy value from
+     * @param to the message object to copy value to
+     */
+    static void copyStreamId(final HttpMessage from, final HttpMessage to) {
+        final var streamId = from.headers().getInt(STREAM_ID.text());
+        if (streamId != null) {
+            to.headers().setInt(STREAM_ID.text(), streamId);
+        }
+    }
+}