From 09e214c09c78a48ea96b0137555b3c2a98a1bfab Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Tue, 31 Mar 2020 14:03:17 +0100
Subject: [PATCH] Make the HTTP/2 connection ID and stream Id available to
 applications
Origin: https://github.com/apache/tomcat/commit/09e214c09c78a48ea96b0137555b3c2a98a1bfab

---
 java/org/apache/catalina/Globals.java         | 16 ++++++++
 .../apache/catalina/connector/Request.java    | 27 +++++++++++++
 java/org/apache/coyote/AbstractProcessor.java | 39 +++++++++++++++++++
 java/org/apache/coyote/ActionCode.java        | 14 ++++++-
 .../apache/coyote/http2/StreamProcessor.java  | 12 ++++++
 webapps/docs/changelog.xml                    |  5 +++
 webapps/docs/config/http2.xml                 |  9 +++++
 7 files changed, 121 insertions(+), 1 deletion(-)

diff --git a/java/org/apache/catalina/Globals.java b/java/org/apache/catalina/Globals.java
index 994902b..c19d69c 100644
--- a/java/org/apache/catalina/Globals.java
+++ b/java/org/apache/catalina/Globals.java
@@ -112,6 +112,22 @@ public final class Globals {
         "org.apache.catalina.NAMED";
 
 
+    /**
+     * The request attribute used to expose the current connection ID associated
+     * with the request, if any. Used with multiplexing protocols such as
+     * HTTTP/2.
+     */
+    public static final String CONNECTION_ID = "org.apache.coyote.connectionID";
+
+
+    /**
+     * The request attribute used to expose the current stream ID associated
+     * with the request, if any. Used with multiplexing protocols such as
+     * HTTTP/2.
+     */
+    public static final String STREAM_ID = "org.apache.coyote.streamID";
+ 
+
     /**
      * The servlet context attribute under which we store a flag used
      * to mark this request as having been processed by the SSIServlet.
diff --git a/java/org/apache/catalina/connector/Request.java b/java/org/apache/catalina/connector/Request.java
index c4cc26a..94065ef 100644
--- a/java/org/apache/catalina/connector/Request.java
+++ b/java/org/apache/catalina/connector/Request.java
@@ -40,6 +40,7 @@ import java.util.TimeZone;
 import java.util.TreeMap;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
 
 import javax.naming.NamingException;
 import javax.security.auth.Subject;
@@ -3487,6 +3488,32 @@ public class Request implements HttpServletRequest {
                         // NO-OP
                     }
                 });
+        specialAttributes.put(Globals.CONNECTION_ID,
+                new SpecialAttributeAdapter() {
+                    @Override
+                    public Object get(Request request, String name) {
+                        AtomicReference<Object> result = new AtomicReference<>();
+                        request.getCoyoteRequest().action(ActionCode.CONNECTION_ID, result);
+                        return result.get();
+                    }
+                    @Override
+                    public void set(Request request, String name, Object value) {
+                        // NO-OP
+                    }
+                });
+        specialAttributes.put(Globals.STREAM_ID,
+                new SpecialAttributeAdapter() {
+                    @Override
+                    public Object get(Request request, String name) {
+                        AtomicReference<Object> result = new AtomicReference<>();
+                        request.getCoyoteRequest().action(ActionCode.STREAM_ID, result);
+                        return result.get();
+                    }
+                    @Override
+                    public void set(Request request, String name, Object value) {
+                        // NO-OP
+                    }
+                });
 
         for (SimpleDateFormat sdf : formatsTemplate) {
             sdf.setTimeZone(GMT_ZONE);
diff --git a/java/org/apache/coyote/AbstractProcessor.java b/java/org/apache/coyote/AbstractProcessor.java
index 5be2cb8..b351cb7 100644
--- a/java/org/apache/coyote/AbstractProcessor.java
+++ b/java/org/apache/coyote/AbstractProcessor.java
@@ -22,6 +22,7 @@ import java.nio.ByteBuffer;
 import java.util.Iterator;
 import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
 
 import javax.servlet.RequestDispatcher;
 
@@ -589,6 +590,20 @@ public abstract class AbstractProcessor extends AbstractProcessorLight implement
             result.set(isTrailerFieldsSupported());
             break;
         }
+
+        // Identifiers associated with multiplexing protocols like HTTP/2
+        case CONNECTION_ID: {
+            @SuppressWarnings("unchecked")
+            AtomicReference<Object> result = (AtomicReference<Object>) param;
+            result.set(getConnectionID());
+            break;
+        }
+        case STREAM_ID: {
+            @SuppressWarnings("unchecked")
+            AtomicReference<Object> result = (AtomicReference<Object>) param;
+            result.set(getStreamID());
+            break;
+        }
         }
     }
 
@@ -889,6 +904,30 @@ public abstract class AbstractProcessor extends AbstractProcessorLight implement
     }
 
 
+    /**
+     * Protocols that support multiplexing (e.g. HTTP/2) should override this
+     * method and return the appropriate ID.
+     *
+     * @return The stream ID associated with this request or {@code null} if a
+     *         multiplexing protocol is not being used
+      */
+    protected Object getConnectionID() {
+        return null;
+    }
+
+
+    /**
+     * Protocols that support multiplexing (e.g. HTTP/2) should override this
+     * method and return the appropriate ID.
+     *
+     * @return The stream ID associated with this request or {@code null} if a
+     *         multiplexing protocol is not being used
+     */
+    protected Object getStreamID() {
+        return null;
+    }
+
+
     /**
      * Flush any pending writes. Used during non-blocking writes to flush any
      * remaining data from a previous incomplete write.
diff --git a/java/org/apache/coyote/ActionCode.java b/java/org/apache/coyote/ActionCode.java
index 3ff4c21..5c5af4f 100644
--- a/java/org/apache/coyote/ActionCode.java
+++ b/java/org/apache/coyote/ActionCode.java
@@ -265,5 +265,17 @@ public enum ActionCode {
      * once an HTTP/1.1 response has been committed, it will no longer support
      * trailer fields.
      */
-    IS_TRAILER_FIELDS_SUPPORTED
+    IS_TRAILER_FIELDS_SUPPORTED,
+
+    /**
+     * Obtain the connection identifier for the request. Used with multiplexing
+     * protocols such as HTTP/2.
+     */
+    CONNECTION_ID,
+
+    /**
+     * Obtain the stream identifier for the request. Used with multiplexing
+     * protocols such as HTTP/2.
+     */
+    STREAM_ID
 }
diff --git a/java/org/apache/coyote/http2/StreamProcessor.java b/java/org/apache/coyote/http2/StreamProcessor.java
index d9c1c82..fd833ec 100644
--- a/java/org/apache/coyote/http2/StreamProcessor.java
+++ b/java/org/apache/coyote/http2/StreamProcessor.java
@@ -300,6 +300,18 @@ class StreamProcessor extends AbstractProcessor {
     }
 
 
+    @Override
+    protected Object getConnectionID() {
+        return stream.getConnectionId();
+    }
+
+
+    @Override
+    protected Object getStreamID() {
+        return stream.getIdentifier().toString();
+    }
+
+
     @Override
     public final void recycle() {
         // StreamProcessor instances are not re-used.
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index a97e15d..8837628 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -247,6 +247,11 @@
         Ensure that the HTTP/1.1 processor is correctly recycled when a direct
         connection to h2c is made. (markt)
       </fix>
+      <add>
+        Expose the HTTP/2 connection ID and stream ID to applications via the
+        request attributes <code>org.apache.coyote.connectionID</code> and
+        <code>org.apache.coyote.streamID</code> respectively. (markt)
+      </add>
     </changelog>
   </subsection>
   <subsection name="Jasper">
diff --git a/webapps/docs/config/http2.xml b/webapps/docs/config/http2.xml
index 83b8acd..145a184 100644
--- a/webapps/docs/config/http2.xml
+++ b/webapps/docs/config/http2.xml
@@ -44,6 +44,15 @@
   the Servlet API is fundamentally blocking, each HTTP/2 stream requires a
   dedicated container thread for the duration of that stream.</p>
 
+  <p>Requests processed using HTTP/2 will have the following additional request
+  attributes available:</p>
+  <ul>
+    <li><code>org.apache.coyote.connectionID</code> will return the HTTP/2
+        connection ID</li>
+    <li><code>org.apache.coyote.streamID</code> will return the HTTP/2 stream
+        ID</li>
+  </ul>
+
 </section>
 
 
-- 
2.33.0

