You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2020/10/15 09:19:04 UTC
[tomcat] branch 9.0.x updated: Complete fix for BZ 63362. Collect
stats for h2, websocket and upgrade
This is an automated email from the ASF dual-hosted git repository.
markt pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/9.0.x by this push:
new 41497b9 Complete fix for BZ 63362. Collect stats for h2, websocket and upgrade
41497b9 is described below
commit 41497b9883efdc64c210e659235d853f32331127
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Thu Oct 15 09:45:47 2020 +0100
Complete fix for BZ 63362. Collect stats for h2, websocket and upgrade
https://bz.apache.org/bugzilla/show_bug.cgi?id=63362
---
java/org/apache/catalina/connector/Request.java | 2 +-
java/org/apache/coyote/AbstractProtocol.java | 17 ---
java/org/apache/coyote/LocalStrings.properties | 1 -
java/org/apache/coyote/UpgradeProtocol.java | 23 +---
java/org/apache/coyote/UpgradeToken.java | 9 +-
.../coyote/http11/AbstractHttp11Protocol.java | 96 ++++++++++++++++-
java/org/apache/coyote/http11/Http11Processor.java | 2 +-
.../apache/coyote/http11/LocalStrings.properties | 2 +
.../http11/upgrade/InternalHttpUpgradeHandler.java | 4 +
.../coyote/http11/upgrade/UpgradeGroupInfo.java | 120 +++++++++++++++++++++
.../apache/coyote/http11/upgrade/UpgradeInfo.java | 96 +++++++++++++++++
.../http11/upgrade/UpgradeProcessorExternal.java | 14 ++-
.../http11/upgrade/UpgradeProcessorInternal.java | 12 ++-
.../http11/upgrade/UpgradeServletInputStream.java | 17 ++-
.../http11/upgrade/UpgradeServletOutputStream.java | 7 +-
java/org/apache/coyote/http2/Http2Protocol.java | 49 ++++-----
.../apache/coyote/http2/LocalStrings.properties | 2 +
java/org/apache/coyote/http2/StreamProcessor.java | 6 +-
java/org/apache/coyote/mbeans-descriptors.xml | 30 ++++++
java/org/apache/tomcat/websocket/WsFrameBase.java | 13 +++
.../tomcat/websocket/WsRemoteEndpointImplBase.java | 15 +++
.../tomcat/websocket/server/WsFrameServer.java | 14 ++-
.../websocket/server/WsHttpUpgradeHandler.java | 12 ++-
.../server/WsRemoteEndpointImplServer.java | 12 ++-
.../apache/coyote/http11/upgrade/TestUpgrade.java | 4 +
webapps/docs/changelog.xml | 4 +-
26 files changed, 489 insertions(+), 94 deletions(-)
diff --git a/java/org/apache/catalina/connector/Request.java b/java/org/apache/catalina/connector/Request.java
index dc6faaa..0bd422b 100644
--- a/java/org/apache/catalina/connector/Request.java
+++ b/java/org/apache/catalina/connector/Request.java
@@ -2049,7 +2049,7 @@ public class Request implements HttpServletRequest {
throw new ServletException(e);
}
UpgradeToken upgradeToken = new UpgradeToken(handler,
- getContext(), instanceManager);
+ getContext(), instanceManager, response.getHeader("upgrade"));
coyoteRequest.action(ActionCode.UPGRADE, upgradeToken);
diff --git a/java/org/apache/coyote/AbstractProtocol.java b/java/org/apache/coyote/AbstractProtocol.java
index ab9dd38..28eb9e9 100644
--- a/java/org/apache/coyote/AbstractProtocol.java
+++ b/java/org/apache/coyote/AbstractProtocol.java
@@ -582,13 +582,6 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
endpoint.setDomain(domain);
endpoint.init();
-
- UpgradeProtocol[] upgradeProtocols = findUpgradeProtocols();
- for (UpgradeProtocol upgradeProtocol : upgradeProtocols) {
- // Implementation note: Failure of one upgrade protocol fails the
- // whole Connector
- upgradeProtocol.init();
- }
}
@@ -702,16 +695,6 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
logPortOffset();
}
- UpgradeProtocol[] upgradeProtocols = findUpgradeProtocols();
- for (UpgradeProtocol upgradeProtocol : upgradeProtocols) {
- try {
- upgradeProtocol.destroy();
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- getLog().error(sm.getString("abstractProtocol.upgradeProtocolDestroyError"), t);
- }
- }
-
try {
endpoint.destroy();
} finally {
diff --git a/java/org/apache/coyote/LocalStrings.properties b/java/org/apache/coyote/LocalStrings.properties
index 43c8d64..83960cb 100644
--- a/java/org/apache/coyote/LocalStrings.properties
+++ b/java/org/apache/coyote/LocalStrings.properties
@@ -36,7 +36,6 @@ abstractProcessor.socket.ssl=Exception getting SSL attributes
abstractProtocol.mbeanDeregistrationFailed=Failed to deregister MBean named [{0}] from MBean server [{1}]
abstractProtocol.processorRegisterError=Error registering request processor
abstractProtocol.processorUnregisterError=Error unregistering request processor
-abstractProtocol.upgradeProtocolDestroyError=Error destroying upgrade protocol
abstractProtocol.waitingProcessor.add=Added processor [{0}] to waiting processors
abstractProtocol.waitingProcessor.remove=Removed processor [{0}] from waiting processors
diff --git a/java/org/apache/coyote/UpgradeProtocol.java b/java/org/apache/coyote/UpgradeProtocol.java
index 50fe6c3..5167996 100644
--- a/java/org/apache/coyote/UpgradeProtocol.java
+++ b/java/org/apache/coyote/UpgradeProtocol.java
@@ -109,6 +109,7 @@ public interface UpgradeProtocol {
// NO-OP
}
+
/**
* Configure the HTTP/1.1 protocol that this UpgradeProcotol is nested
* under. Connections passed to this UpgradeProtocol via HTTP upgrade will
@@ -131,26 +132,4 @@ public interface UpgradeProtocol {
setHttp11Protocol((AbstractHttp11Protocol<?>) protocol);
}
}
-
-
- /**
- * Initialise the upgrade protocol. Called once the parent HTTP/1.1 protocol
- * has initialised.
- *
- * @throws Exception If initialisation fails
- */
- public default void init() throws Exception {
- // NO-OP
- }
-
-
- /**
- * Destroy the upgrade protocol. Called before the parent HTTP/1.1 protocol
- * is destroyed.
- *
- * @throws Exception If the upgrade protocol is not destroyed cleanly
- */
- public default void destroy() throws Exception {
- // NO-OP
- }
}
diff --git a/java/org/apache/coyote/UpgradeToken.java b/java/org/apache/coyote/UpgradeToken.java
index cf297fb..7d9d680 100644
--- a/java/org/apache/coyote/UpgradeToken.java
+++ b/java/org/apache/coyote/UpgradeToken.java
@@ -30,12 +30,14 @@ public final class UpgradeToken {
private final ContextBind contextBind;
private final HttpUpgradeHandler httpUpgradeHandler;
private final InstanceManager instanceManager;
+ private final String protocol;
- public UpgradeToken(HttpUpgradeHandler httpUpgradeHandler,
- ContextBind contextBind, InstanceManager instanceManager) {
+ public UpgradeToken(HttpUpgradeHandler httpUpgradeHandler, ContextBind contextBind, InstanceManager instanceManager,
+ String protocol) {
this.contextBind = contextBind;
this.httpUpgradeHandler = httpUpgradeHandler;
this.instanceManager = instanceManager;
+ this.protocol = protocol;
}
public final ContextBind getContextBind() {
@@ -50,4 +52,7 @@ public final class UpgradeToken {
return instanceManager;
}
+ public final String getProtocol() {
+ return protocol;
+ }
}
diff --git a/java/org/apache/coyote/http11/AbstractHttp11Protocol.java b/java/org/apache/coyote/http11/AbstractHttp11Protocol.java
index 2ed2f22..0c82380 100644
--- a/java/org/apache/coyote/http11/AbstractHttp11Protocol.java
+++ b/java/org/apache/coyote/http11/AbstractHttp11Protocol.java
@@ -27,6 +27,8 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
import javax.servlet.http.HttpUpgradeHandler;
import org.apache.coyote.AbstractProtocol;
@@ -38,9 +40,12 @@ import org.apache.coyote.Response;
import org.apache.coyote.UpgradeProtocol;
import org.apache.coyote.UpgradeToken;
import org.apache.coyote.http11.upgrade.InternalHttpUpgradeHandler;
+import org.apache.coyote.http11.upgrade.UpgradeGroupInfo;
import org.apache.coyote.http11.upgrade.UpgradeProcessorExternal;
import org.apache.coyote.http11.upgrade.UpgradeProcessorInternal;
import org.apache.tomcat.util.buf.StringUtils;
+import org.apache.tomcat.util.modeler.Registry;
+import org.apache.tomcat.util.modeler.Util;
import org.apache.tomcat.util.net.AbstractEndpoint;
import org.apache.tomcat.util.net.SSLHostConfig;
import org.apache.tomcat.util.net.SocketWrapperBase;
@@ -73,6 +78,31 @@ public abstract class AbstractHttp11Protocol<S> extends AbstractProtocol<S> {
}
super.init();
+
+ // Set the Http11Protocol (i.e. this) for any upgrade protocols once
+ // this has completed initialisation as the upgrade protocols may expect this
+ // to be initialised when the call is made
+ for (UpgradeProtocol upgradeProtocol : upgradeProtocols) {
+ upgradeProtocol.setHttp11Protocol(this);
+ }
+ }
+
+
+ @Override
+ public void destroy() throws Exception {
+ // There may be upgrade protocols with their own MBeans. These need to
+ // be de-registered.
+ ObjectName rgOname = getGlobalRequestProcessorMBeanName();
+ if (rgOname != null) {
+ Registry registry = Registry.getRegistry(null, null);
+ ObjectName query = new ObjectName(rgOname.getCanonicalName() + ",Upgrade=*");
+ Set<ObjectInstance> upgrades = registry.getMBeanServer().queryMBeans(query, null);
+ for (ObjectInstance upgrade : upgrades) {
+ registry.unregisterComponent(upgrade.getObjectName());
+ }
+ }
+
+ super.destroy();
}
@@ -538,8 +568,6 @@ public abstract class AbstractHttp11Protocol<S> extends AbstractProtocol<S> {
}
}
}
-
- upgradeProtocol.setHttp11Protocol(this);
}
@Override
public UpgradeProtocol getNegotiatedProtocol(String negotiatedName) {
@@ -551,6 +579,66 @@ public abstract class AbstractHttp11Protocol<S> extends AbstractProtocol<S> {
}
+ /**
+ * Map of upgrade protocol name to {@link UpgradeGroupInfo} instance.
+ * <p>
+ * HTTP upgrades via
+ * {@link javax.servlet.http.HttpServletRequest#upgrade(Class)} do not have
+ * to depend on an {@code UpgradeProtocol}. To enable basic statistics to be
+ * made available for these protocols, a map of protocol name to
+ * {@link UpgradeGroupInfo} instances is maintained here.
+ */
+ private final Map<String,UpgradeGroupInfo> upgradeProtocolGroupInfos = new ConcurrentHashMap<>();
+ public UpgradeGroupInfo getUpgradeGroupInfo(String upgradeProtocol) {
+ if (upgradeProtocol == null) {
+ return null;
+ }
+ UpgradeGroupInfo result = upgradeProtocolGroupInfos.get(upgradeProtocol);
+ if (result == null) {
+ // Protecting against multiple JMX registration, not modification
+ // of the Map.
+ synchronized (upgradeProtocolGroupInfos) {
+ result = upgradeProtocolGroupInfos.get(upgradeProtocol);
+ if (result == null) {
+ result = new UpgradeGroupInfo();
+ upgradeProtocolGroupInfos.put(upgradeProtocol, result);
+ ObjectName oname = getONameForUpgrade(upgradeProtocol);
+ if (oname != null) {
+ try {
+ Registry.getRegistry(null, null).registerComponent(result, oname, null);
+ } catch (Exception e) {
+ getLog().warn(sm.getString("abstractHttp11Protocol.upgradeJmxRegistrationFail"), e);
+ result = null;
+ }
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+
+ public ObjectName getONameForUpgrade(String upgradeProtocol) {
+ ObjectName oname = null;
+ ObjectName parentRgOname = getGlobalRequestProcessorMBeanName();
+ if (parentRgOname != null) {
+ StringBuilder name = new StringBuilder(parentRgOname.getCanonicalName());
+ name.append(",Upgrade=");
+ if (Util.objectNameValueNeedsQuote(upgradeProtocol)) {
+ name.append(ObjectName.quote(upgradeProtocol));
+ } else {
+ name.append(upgradeProtocol);
+ }
+ try {
+ oname = new ObjectName(name.toString());
+ } catch (Exception e) {
+ getLog().warn(sm.getString("abstractHttp11Protocol.upgradeJmxNameFail"), e);
+ }
+ }
+ return oname;
+ }
+
+
// ------------------------------------------------ HTTP specific properties
// ------------------------------------------ passed through to the EndPoint
@@ -1009,9 +1097,9 @@ public abstract class AbstractHttp11Protocol<S> extends AbstractProtocol<S> {
UpgradeToken upgradeToken) {
HttpUpgradeHandler httpUpgradeHandler = upgradeToken.getHttpUpgradeHandler();
if (httpUpgradeHandler instanceof InternalHttpUpgradeHandler) {
- return new UpgradeProcessorInternal(socket, upgradeToken);
+ return new UpgradeProcessorInternal(socket, upgradeToken, getUpgradeGroupInfo(upgradeToken.getProtocol()));
} else {
- return new UpgradeProcessorExternal(socket, upgradeToken);
+ return new UpgradeProcessorExternal(socket, upgradeToken, getUpgradeGroupInfo(upgradeToken.getProtocol()));
}
}
}
diff --git a/java/org/apache/coyote/http11/Http11Processor.java b/java/org/apache/coyote/http11/Http11Processor.java
index fa9b290..f8dd3c2 100644
--- a/java/org/apache/coyote/http11/Http11Processor.java
+++ b/java/org/apache/coyote/http11/Http11Processor.java
@@ -336,7 +336,7 @@ public class Http11Processor extends AbstractProcessor {
InternalHttpUpgradeHandler upgradeHandler =
upgradeProtocol.getInternalUpgradeHandler(
socketWrapper, getAdapter(), cloneRequest(request));
- UpgradeToken upgradeToken = new UpgradeToken(upgradeHandler, null, null);
+ UpgradeToken upgradeToken = new UpgradeToken(upgradeHandler, null, null, requestedProtocol);
action(ActionCode.UPGRADE, upgradeToken);
return SocketState.UPGRADING;
}
diff --git a/java/org/apache/coyote/http11/LocalStrings.properties b/java/org/apache/coyote/http11/LocalStrings.properties
index 724a99b..d880c71 100644
--- a/java/org/apache/coyote/http11/LocalStrings.properties
+++ b/java/org/apache/coyote/http11/LocalStrings.properties
@@ -16,6 +16,8 @@
abstractHttp11Protocol.alpnConfigured=The [{0}] connector has been configured to support negotiation to [{1}] via ALPN
abstractHttp11Protocol.alpnWithNoAlpn=The upgrade handler [{0}] for [{1}] only supports upgrade via ALPN but has been configured for the [{2}] connector that does not support ALPN.
abstractHttp11Protocol.httpUpgradeConfigured=The [{0}] connector has been configured to support HTTP upgrade to [{1}]
+abstractHttp11Protocol.upgradeJmxNameFail=Failed to create ObjectName with which to register upgrade protocol in JMX
+abstractHttp11Protocol.upgradeJmxRegistrationFail=Failed to register upgrade protocol in JMX
http11processor.fallToDebug=\n\
\ Note: further occurrences of HTTP request parsing errors will be logged at DEBUG level.
diff --git a/java/org/apache/coyote/http11/upgrade/InternalHttpUpgradeHandler.java b/java/org/apache/coyote/http11/upgrade/InternalHttpUpgradeHandler.java
index e7d3453..f8f5f4c 100644
--- a/java/org/apache/coyote/http11/upgrade/InternalHttpUpgradeHandler.java
+++ b/java/org/apache/coyote/http11/upgrade/InternalHttpUpgradeHandler.java
@@ -43,4 +43,8 @@ public interface InternalHttpUpgradeHandler extends HttpUpgradeHandler {
default boolean hasAsyncIO() {
return false;
}
+
+ default UpgradeInfo getUpgradeInfo() {
+ return null;
+ }
}
\ No newline at end of file
diff --git a/java/org/apache/coyote/http11/upgrade/UpgradeGroupInfo.java b/java/org/apache/coyote/http11/upgrade/UpgradeGroupInfo.java
new file mode 100644
index 0000000..7d72976
--- /dev/null
+++ b/java/org/apache/coyote/http11/upgrade/UpgradeGroupInfo.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.coyote.http11.upgrade;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.tomcat.util.modeler.BaseModelMBean;
+
+/**
+ * This aggregates the data collected from each UpgradeInfo instance.
+ */
+public class UpgradeGroupInfo extends BaseModelMBean {
+
+ private final List<UpgradeInfo> upgradeInfos = new ArrayList<>();
+
+ private long deadBytesReceived = 0;
+ private long deadBytesSent = 0;
+ private long deadMsgsReceived = 0;
+ private long deadMsgsSent = 0;
+
+
+ public synchronized void addUpgradeInfo(UpgradeInfo ui) {
+ upgradeInfos.add(ui);
+ }
+
+
+ public synchronized void removeUpgradeInfo(UpgradeInfo ui) {
+ if (ui != null) {
+ deadBytesReceived += ui.getBytesReceived();
+ deadBytesSent += ui.getBytesSent();
+ deadMsgsReceived += ui.getMsgsReceived();
+ deadMsgsSent += ui.getMsgsSent();
+
+ upgradeInfos.remove(ui);
+ }
+ }
+
+
+ public synchronized long getBytesReceived() {
+ long bytes = deadBytesReceived;
+ for (UpgradeInfo ui : upgradeInfos) {
+ bytes += ui.getBytesReceived();
+ }
+ return bytes;
+ }
+ public synchronized void setBytesReceived(long bytesReceived) {
+ deadBytesReceived = bytesReceived;
+ for (UpgradeInfo ui : upgradeInfos) {
+ ui.setBytesReceived(bytesReceived);
+ }
+ }
+
+
+ public synchronized long getBytesSent() {
+ long bytes = deadBytesSent;
+ for (UpgradeInfo ui : upgradeInfos) {
+ bytes += ui.getBytesSent();
+ }
+ return bytes;
+ }
+ public synchronized void setBytesSent(long bytesSent) {
+ deadBytesSent = bytesSent;
+ for (UpgradeInfo ui : upgradeInfos) {
+ ui.setBytesSent(bytesSent);
+ }
+ }
+
+
+ public synchronized long getMsgsReceived() {
+ long msgs = deadMsgsReceived;
+ for (UpgradeInfo ui : upgradeInfos) {
+ msgs += ui.getMsgsReceived();
+ }
+ return msgs;
+ }
+ public synchronized void setMsgsReceived(long msgsReceived) {
+ deadMsgsReceived = msgsReceived;
+ for (UpgradeInfo ui : upgradeInfos) {
+ ui.setMsgsReceived(msgsReceived);
+ }
+ }
+
+
+ public synchronized long getMsgsSent() {
+ long msgs = deadMsgsSent;
+ for (UpgradeInfo ui : upgradeInfos) {
+ msgs += ui.getMsgsSent();
+ }
+ return msgs;
+ }
+ public synchronized void setMsgsSent(long msgsSent) {
+ deadMsgsSent = msgsSent;
+ for (UpgradeInfo ui : upgradeInfos) {
+ ui.setMsgsSent(msgsSent);
+ }
+ }
+
+
+ public void resetCounters() {
+ setBytesReceived(0);
+ setBytesSent(0);
+ setMsgsReceived(0);
+ setMsgsSent(0);
+ }
+}
diff --git a/java/org/apache/coyote/http11/upgrade/UpgradeInfo.java b/java/org/apache/coyote/http11/upgrade/UpgradeInfo.java
new file mode 100644
index 0000000..eb3313c
--- /dev/null
+++ b/java/org/apache/coyote/http11/upgrade/UpgradeInfo.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.coyote.http11.upgrade;
+
+/**
+ * Structure to hold statistical information about connections that have been
+ * established using the HTTP/1.1 upgrade mechanism. Bytes sent/received will
+ * always be populated. Messages sent/received will be populated if that makes
+ * sense for the protocol and the information is exposed by the protocol
+ * implementation.
+ */
+public class UpgradeInfo {
+
+ private UpgradeGroupInfo groupInfo = null;
+ private volatile long bytesSent = 0;
+ private volatile long bytesReceived = 0;
+ private volatile long msgsSent = 0;
+ private volatile long msgsReceived = 0;
+
+
+
+ public UpgradeGroupInfo getGlobalProcessor() {
+ return groupInfo;
+ }
+
+
+ public void setGroupInfo(UpgradeGroupInfo groupInfo) {
+ if (groupInfo == null) {
+ if (this.groupInfo != null) {
+ this.groupInfo.removeUpgradeInfo(this);
+ this.groupInfo = null;
+ }
+ } else {
+ this.groupInfo = groupInfo;
+ groupInfo.addUpgradeInfo(this);
+ }
+ }
+
+
+ public long getBytesSent() {
+ return bytesSent;
+ }
+ public void setBytesSent(long bytesSent) {
+ this.bytesSent = bytesSent;
+ }
+ public void addBytesSent(long bytesSent) {
+ this.bytesSent += bytesSent;
+ }
+
+
+ public long getBytesReceived() {
+ return bytesReceived;
+ }
+ public void setBytesReceived(long bytesReceived) {
+ this.bytesReceived = bytesReceived;
+ }
+ public void addBytesReceived(long bytesReceived) {
+ this.bytesReceived += bytesReceived;
+ }
+
+
+ public long getMsgsSent() {
+ return msgsSent;
+ }
+ public void setMsgsSent(long msgsSent) {
+ this.msgsSent = msgsSent;
+ }
+ public void addMsgsSent(long msgsSent) {
+ this.msgsSent += msgsSent;
+ }
+
+
+ public long getMsgsReceived() {
+ return msgsReceived;
+ }
+ public void setMsgsReceived(long msgsReceived) {
+ this.msgsReceived = msgsReceived;
+ }
+ public void addMsgsReceived(long msgsReceived) {
+ this.msgsReceived += msgsReceived;
+ }
+}
diff --git a/java/org/apache/coyote/http11/upgrade/UpgradeProcessorExternal.java b/java/org/apache/coyote/http11/upgrade/UpgradeProcessorExternal.java
index b73f4e7..e642721 100644
--- a/java/org/apache/coyote/http11/upgrade/UpgradeProcessorExternal.java
+++ b/java/org/apache/coyote/http11/upgrade/UpgradeProcessorExternal.java
@@ -37,13 +37,15 @@ public class UpgradeProcessorExternal extends UpgradeProcessorBase {
private final UpgradeServletInputStream upgradeServletInputStream;
private final UpgradeServletOutputStream upgradeServletOutputStream;
+ private final UpgradeInfo upgradeInfo;
-
- public UpgradeProcessorExternal(SocketWrapperBase<?> wrapper,
- UpgradeToken upgradeToken) {
+ public UpgradeProcessorExternal(SocketWrapperBase<?> wrapper, UpgradeToken upgradeToken,
+ UpgradeGroupInfo upgradeGroupInfo) {
super(upgradeToken);
- this.upgradeServletInputStream = new UpgradeServletInputStream(this, wrapper);
- this.upgradeServletOutputStream = new UpgradeServletOutputStream(this, wrapper);
+ this.upgradeInfo = new UpgradeInfo();
+ upgradeGroupInfo.addUpgradeInfo(upgradeInfo);
+ this.upgradeServletInputStream = new UpgradeServletInputStream(this, wrapper, upgradeInfo);
+ this.upgradeServletOutputStream = new UpgradeServletOutputStream(this, wrapper, upgradeInfo);
/*
* Leave timeouts in the hands of the upgraded protocol.
@@ -65,6 +67,8 @@ public class UpgradeProcessorExternal extends UpgradeProcessorBase {
public void close() throws Exception {
upgradeServletInputStream.close();
upgradeServletOutputStream.close();
+ // Triggers update of stats from UpgradeInfo to UpgradeGroupInfo
+ upgradeInfo.setGroupInfo(null);
}
diff --git a/java/org/apache/coyote/http11/upgrade/UpgradeProcessorInternal.java b/java/org/apache/coyote/http11/upgrade/UpgradeProcessorInternal.java
index 0e99265..1a37cea 100644
--- a/java/org/apache/coyote/http11/upgrade/UpgradeProcessorInternal.java
+++ b/java/org/apache/coyote/http11/upgrade/UpgradeProcessorInternal.java
@@ -35,8 +35,8 @@ public class UpgradeProcessorInternal extends UpgradeProcessorBase {
private final InternalHttpUpgradeHandler internalHttpUpgradeHandler;
- public UpgradeProcessorInternal(SocketWrapperBase<?> wrapper,
- UpgradeToken upgradeToken) {
+ public UpgradeProcessorInternal(SocketWrapperBase<?> wrapper, UpgradeToken upgradeToken,
+ UpgradeGroupInfo upgradeGroupInfo) {
super(upgradeToken);
this.internalHttpUpgradeHandler = (InternalHttpUpgradeHandler) upgradeToken.getHttpUpgradeHandler();
/*
@@ -46,6 +46,10 @@ public class UpgradeProcessorInternal extends UpgradeProcessorBase {
wrapper.setWriteTimeout(INFINITE_TIMEOUT);
internalHttpUpgradeHandler.setSocketWrapper(wrapper);
+ UpgradeInfo upgradeInfo = internalHttpUpgradeHandler.getUpgradeInfo();
+ if (upgradeInfo != null && upgradeGroupInfo != null) {
+ upgradeInfo.setGroupInfo(upgradeGroupInfo);
+ }
}
@@ -88,6 +92,10 @@ public class UpgradeProcessorInternal extends UpgradeProcessorBase {
@Override
public void close() throws Exception {
+ UpgradeInfo upgradeInfo = internalHttpUpgradeHandler.getUpgradeInfo();
+ if (upgradeInfo != null) {
+ upgradeInfo.setGroupInfo(null);
+ }
internalHttpUpgradeHandler.destroy();
}
diff --git a/java/org/apache/coyote/http11/upgrade/UpgradeServletInputStream.java b/java/org/apache/coyote/http11/upgrade/UpgradeServletInputStream.java
index 1c1ddb6..387581a 100644
--- a/java/org/apache/coyote/http11/upgrade/UpgradeServletInputStream.java
+++ b/java/org/apache/coyote/http11/upgrade/UpgradeServletInputStream.java
@@ -37,6 +37,7 @@ public class UpgradeServletInputStream extends ServletInputStream {
private final UpgradeProcessorBase processor;
private final SocketWrapperBase<?> socketWrapper;
+ private final UpgradeInfo upgradeInfo;
private volatile boolean closed = false;
private volatile boolean eof = false;
@@ -45,10 +46,11 @@ public class UpgradeServletInputStream extends ServletInputStream {
private volatile ReadListener listener = null;
- public UpgradeServletInputStream(UpgradeProcessorBase processor,
- SocketWrapperBase<?> socketWrapper) {
+ public UpgradeServletInputStream(UpgradeProcessorBase processor, SocketWrapperBase<?> socketWrapper,
+ UpgradeInfo upgradeInfo) {
this.processor = processor;
this.socketWrapper = socketWrapper;
+ this.upgradeInfo = upgradeInfo;
}
@@ -139,7 +141,13 @@ public class UpgradeServletInputStream extends ServletInputStream {
break;
}
}
- return count > 0 ? count : -1;
+
+ if (count > 0) {
+ upgradeInfo.addBytesReceived(count);
+ return count;
+ } else {
+ return -1;
+ }
}
@@ -151,6 +159,8 @@ public class UpgradeServletInputStream extends ServletInputStream {
int result = socketWrapper.read(listener == null, b, off, len);
if (result == -1) {
eof = true;
+ } else {
+ upgradeInfo.addBytesReceived(result);
}
return result;
} catch (IOException ioe) {
@@ -197,6 +207,7 @@ public class UpgradeServletInputStream extends ServletInputStream {
eof = true;
return -1;
} else {
+ upgradeInfo.addBytesReceived(1);
return b[0] & 0xFF;
}
}
diff --git a/java/org/apache/coyote/http11/upgrade/UpgradeServletOutputStream.java b/java/org/apache/coyote/http11/upgrade/UpgradeServletOutputStream.java
index 3de8096..84d8fa5 100644
--- a/java/org/apache/coyote/http11/upgrade/UpgradeServletOutputStream.java
+++ b/java/org/apache/coyote/http11/upgrade/UpgradeServletOutputStream.java
@@ -37,6 +37,7 @@ public class UpgradeServletOutputStream extends ServletOutputStream {
private final UpgradeProcessorBase processor;
private final SocketWrapperBase<?> socketWrapper;
+ private final UpgradeInfo upgradeInfo;
// Used to ensure that isReady() and onWritePossible() have a consistent
// view of buffer and registered.
@@ -61,10 +62,11 @@ public class UpgradeServletOutputStream extends ServletOutputStream {
- public UpgradeServletOutputStream(UpgradeProcessorBase processor,
- SocketWrapperBase<?> socketWrapper) {
+ public UpgradeServletOutputStream(UpgradeProcessorBase processor, SocketWrapperBase<?> socketWrapper,
+ UpgradeInfo upgradeInfo) {
this.processor = processor;
this.socketWrapper = socketWrapper;
+ this.upgradeInfo = upgradeInfo;
}
@@ -210,6 +212,7 @@ public class UpgradeServletOutputStream extends ServletOutputStream {
} else {
socketWrapper.write(false, b, off, len);
}
+ upgradeInfo.addBytesSent(len);
}
diff --git a/java/org/apache/coyote/http2/Http2Protocol.java b/java/org/apache/coyote/http2/Http2Protocol.java
index b61fe55..4743b05 100644
--- a/java/org/apache/coyote/http2/Http2Protocol.java
+++ b/java/org/apache/coyote/http2/Http2Protocol.java
@@ -42,12 +42,18 @@ import org.apache.coyote.UpgradeToken;
import org.apache.coyote.http11.AbstractHttp11Protocol;
import org.apache.coyote.http11.upgrade.InternalHttpUpgradeHandler;
import org.apache.coyote.http11.upgrade.UpgradeProcessorInternal;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.buf.StringUtils;
import org.apache.tomcat.util.modeler.Registry;
import org.apache.tomcat.util.net.SocketWrapperBase;
+import org.apache.tomcat.util.res.StringManager;
public class Http2Protocol implements UpgradeProtocol {
+ private static final Log log = LogFactory.getLog(Http2Protocol.class);
+ private static final StringManager sm = StringManager.getManager(Http2Protocol.class);
+
static final long DEFAULT_READ_TIMEOUT = 5000;
static final long DEFAULT_WRITE_TIMEOUT = 5000;
static final long DEFAULT_KEEP_ALIVE_TIMEOUT = 20000;
@@ -102,7 +108,6 @@ public class Http2Protocol implements UpgradeProtocol {
private AbstractHttp11Protocol<?> http11Protocol = null;
private RequestGroupInfo global = new RequestGroupInfo();
- private ObjectName rgOname = null;
@Override
public String getHttpUpgradeName(boolean isSSLEnabled) {
@@ -125,8 +130,10 @@ public class Http2Protocol implements UpgradeProtocol {
@Override
public Processor getProcessor(SocketWrapperBase<?> socketWrapper, Adapter adapter) {
+ String upgradeProtocol = getUpgradeProtocolName();
UpgradeProcessorInternal processor = new UpgradeProcessorInternal(socketWrapper,
- new UpgradeToken(getInternalUpgradeHandler(socketWrapper, adapter, null), null, null));
+ new UpgradeToken(getInternalUpgradeHandler(socketWrapper, adapter, null), null, null, upgradeProtocol),
+ http11Protocol.getUpgradeGroupInfo(upgradeProtocol));
return processor;
}
@@ -445,38 +452,26 @@ public class Http2Protocol implements UpgradeProtocol {
@Override
public void setHttp11Protocol(AbstractHttp11Protocol<?> http11Protocol) {
this.http11Protocol = http11Protocol;
- }
-
- public RequestGroupInfo getGlobal() {
- return global;
+ try {
+ ObjectName oname = this.http11Protocol.getONameForUpgrade(getUpgradeProtocolName());
+ Registry.getRegistry(null, null).registerComponent(global, oname, null);
+ } catch (Exception e) {
+ log.warn(sm.getString("http2Protocol.jmxRegistration.fail"), e);
+ }
}
- @Override
- public void init() throws Exception {
- ObjectName parentRgOname = http11Protocol.getGlobalRequestProcessorMBeanName();
- if (parentRgOname != null) {
- StringBuilder name = new StringBuilder(parentRgOname.getCanonicalName());
- name.append(",Upgrade=");
- // Neither of these names need quoting
- if (http11Protocol.isSSLEnabled()) {
- name.append(ALPN_NAME);
- } else {
- name.append(HTTP_UPGRADE_NAME);
- }
- ObjectName rgOname = new ObjectName(name.toString());
- this.rgOname = rgOname;
- Registry.getRegistry(null, null).registerComponent(global, rgOname, null);
+ public String getUpgradeProtocolName() {
+ if (http11Protocol.isSSLEnabled()) {
+ return ALPN_NAME;
+ } else {
+ return HTTP_UPGRADE_NAME;
}
}
- @Override
- public void destroy() throws Exception {
- ObjectName rgOname = this.rgOname;
- if (rgOname != null) {
- Registry.getRegistry(null, null).unregisterComponent(rgOname);
- }
+ public RequestGroupInfo getGlobal() {
+ return global;
}
}
diff --git a/java/org/apache/coyote/http2/LocalStrings.properties b/java/org/apache/coyote/http2/LocalStrings.properties
index ca6e5af..a1c4075 100644
--- a/java/org/apache/coyote/http2/LocalStrings.properties
+++ b/java/org/apache/coyote/http2/LocalStrings.properties
@@ -72,6 +72,8 @@ http2Parser.processFrameWindowUpdate.debug=Connection [{0}], Stream [{1}], Windo
http2Parser.processFrameWindowUpdate.invalidIncrement=Window update frame received with an invalid increment size of [{0}]
http2Parser.swallow.debug=Connection [{0}], Stream [{1}], Swallowed [{2}] bytes
+http2Protocol.jmxRegistration.fail=JMX registration for the HTTP/2 protocol failed
+
pingManager.roundTripTime=Connection [{0}] Round trip time measured as [{1}]ns
stream.clientCancel=Client reset the stream before the response was complete
diff --git a/java/org/apache/coyote/http2/StreamProcessor.java b/java/org/apache/coyote/http2/StreamProcessor.java
index 98c86cb..862ea35 100644
--- a/java/org/apache/coyote/http2/StreamProcessor.java
+++ b/java/org/apache/coyote/http2/StreamProcessor.java
@@ -27,6 +27,7 @@ import org.apache.coyote.ContainerThreadMarker;
import org.apache.coyote.ContinueResponseTiming;
import org.apache.coyote.ErrorState;
import org.apache.coyote.Request;
+import org.apache.coyote.RequestGroupInfo;
import org.apache.coyote.Response;
import org.apache.coyote.http11.filters.GzipOutputFilter;
import org.apache.juli.logging.Log;
@@ -375,7 +376,10 @@ class StreamProcessor extends AbstractProcessor {
// Calling removeRequestProcessor even though the RequestProcesser was
// never added will add the values from the RequestProcessor to the
// running total for the GlobalRequestProcessor
- handler.getProtocol().getGlobal().removeRequestProcessor(request.getRequestProcessor());
+ RequestGroupInfo global = handler.getProtocol().getGlobal();
+ if (global != null) {
+ global.removeRequestProcessor(request.getRequestProcessor());
+ }
// Clear fields that can be cleared to aid GC and trigger NPEs if this
// is reused
diff --git a/java/org/apache/coyote/mbeans-descriptors.xml b/java/org/apache/coyote/mbeans-descriptors.xml
index 2c1713c..e23b15b 100644
--- a/java/org/apache/coyote/mbeans-descriptors.xml
+++ b/java/org/apache/coyote/mbeans-descriptors.xml
@@ -59,4 +59,34 @@
<operation name="resetCounters" description="Reset counters" impact="ACTION" returnType="void"/>
</mbean>
+
+ <mbean name="UpgradeGroupInfo"
+ description="Runtime information of a group of connections upgraded via the HTTP upgrade process"
+ domain="Catalina"
+ group="Connector"
+ type="org.apache.coyote.http11.upgrade.UpgradeGroupInfo">
+
+ <attribute name="bytesReceived"
+ description="Amount of data received, in bytes"
+ type="long"
+ writeable="false"/>
+
+ <attribute name="bytesSent"
+ description="Amount of data sent, in bytes"
+ type="long"
+ writeable="false"/>
+
+ <attribute name="msgsReceived"
+ description="Number of messages received where applicable for the given protocol"
+ type="long"
+ writeable="false"/>
+
+ <attribute name="msgsSent"
+ description="Number of messages sent where applicable for the given protocol"
+ type="long"
+ writeable="false"/>
+
+ <operation name="resetCounters" description="Reset counters" impact="ACTION" returnType="void"/>
+
+ </mbean>
</mbeans-descriptors>
\ No newline at end of file
diff --git a/java/org/apache/tomcat/websocket/WsFrameBase.java b/java/org/apache/tomcat/websocket/WsFrameBase.java
index 3993c6a..cea5bb3 100644
--- a/java/org/apache/tomcat/websocket/WsFrameBase.java
+++ b/java/org/apache/tomcat/websocket/WsFrameBase.java
@@ -307,11 +307,24 @@ public abstract class WsFrameBase {
result = processDataBinary();
}
}
+ if (result) {
+ updateStats(payloadLength);
+ }
checkRoomPayload();
return result;
}
+ /**
+ * Hook for updating server side statistics. Called on every frame received.
+ *
+ * @param payloadLength Size of message payload
+ */
+ protected void updateStats(long payloadLength) {
+ // NO-OP by default
+ }
+
+
private boolean processDataControl() throws IOException {
TransformationResult tr = transformation.getMoreData(opCode, fin, rsv, controlBufferBinary);
if (TransformationResult.UNDERFLOW.equals(tr)) {
diff --git a/java/org/apache/tomcat/websocket/WsRemoteEndpointImplBase.java b/java/org/apache/tomcat/websocket/WsRemoteEndpointImplBase.java
index 103f80d..75f90e6 100644
--- a/java/org/apache/tomcat/websocket/WsRemoteEndpointImplBase.java
+++ b/java/org/apache/tomcat/websocket/WsRemoteEndpointImplBase.java
@@ -491,6 +491,7 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
mask = null;
}
+ int payloadSize = mp.getPayload().remaining();
headerBuffer.clear();
writeHeader(headerBuffer, mp.isFin(), mp.getRsv(), mp.getOpCode(),
isMasked(), mp.getPayload(), mask, first);
@@ -508,6 +509,20 @@ public abstract class WsRemoteEndpointImplBase implements RemoteEndpoint {
doWrite(mp.getEndHandler(), mp.getBlockingWriteTimeoutExpiry(),
headerBuffer, mp.getPayload());
}
+
+ updateStats(payloadSize);
+ }
+
+
+ /**
+ * Hook for updating server side statistics. Called on every frame written
+ * (including when batching is enabled and the frames are buffered locally
+ * until the buffer is full or is flushed).
+ *
+ * @param payloadLength Size of message payload
+ */
+ protected void updateStats(long payloadLength) {
+ // NO-OP by default
}
diff --git a/java/org/apache/tomcat/websocket/server/WsFrameServer.java b/java/org/apache/tomcat/websocket/server/WsFrameServer.java
index c1e369a..d29ea04 100644
--- a/java/org/apache/tomcat/websocket/server/WsFrameServer.java
+++ b/java/org/apache/tomcat/websocket/server/WsFrameServer.java
@@ -20,6 +20,7 @@ import java.io.EOFException;
import java.io.IOException;
import java.nio.ByteBuffer;
+import org.apache.coyote.http11.upgrade.UpgradeInfo;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
@@ -37,13 +38,15 @@ public class WsFrameServer extends WsFrameBase {
private static final StringManager sm = StringManager.getManager(WsFrameServer.class);
private final SocketWrapperBase<?> socketWrapper;
+ private final UpgradeInfo upgradeInfo;
private final ClassLoader applicationClassLoader;
- public WsFrameServer(SocketWrapperBase<?> socketWrapper, WsSession wsSession,
+ public WsFrameServer(SocketWrapperBase<?> socketWrapper, UpgradeInfo upgradeInfo, WsSession wsSession,
Transformation transformation, ClassLoader applicationClassLoader) {
super(wsSession, transformation);
this.socketWrapper = socketWrapper;
+ this.upgradeInfo = upgradeInfo;
this.applicationClassLoader = applicationClassLoader;
}
@@ -85,6 +88,13 @@ public class WsFrameServer extends WsFrameBase {
@Override
+ protected void updateStats(long payloadLength) {
+ upgradeInfo.addMsgsReceived(1);
+ upgradeInfo.addBytesReceived(payloadLength);
+ }
+
+
+ @Override
protected boolean isMasked() {
// Data is from the client so it should be masked
return true;
@@ -140,6 +150,7 @@ public class WsFrameServer extends WsFrameBase {
socketWrapper.processSocket(SocketEvent.OPEN_READ, true);
}
+
SocketState notifyDataAvailable() throws IOException {
while (isOpen()) {
switch (getReadState()) {
@@ -167,6 +178,7 @@ public class WsFrameServer extends WsFrameBase {
return SocketState.CLOSED;
}
+
private SocketState doOnDataAvailable() throws IOException {
onDataAvailable();
while (isOpen()) {
diff --git a/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java b/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java
index 84b70c6..414f9bc 100644
--- a/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java
+++ b/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java
@@ -30,6 +30,7 @@ import javax.websocket.Extension;
import javax.websocket.server.ServerEndpointConfig;
import org.apache.coyote.http11.upgrade.InternalHttpUpgradeHandler;
+import org.apache.coyote.http11.upgrade.UpgradeInfo;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
@@ -52,6 +53,7 @@ public class WsHttpUpgradeHandler implements InternalHttpUpgradeHandler {
private final ClassLoader applicationClassLoader;
private SocketWrapperBase<?> socketWrapper;
+ private UpgradeInfo upgradeInfo = new UpgradeInfo();
private Endpoint ep;
private ServerEndpointConfig serverEndpointConfig;
@@ -117,7 +119,7 @@ public class WsHttpUpgradeHandler implements InternalHttpUpgradeHandler {
ClassLoader cl = t.getContextClassLoader();
t.setContextClassLoader(applicationClassLoader);
try {
- wsRemoteEndpointServer = new WsRemoteEndpointImplServer(socketWrapper, webSocketContainer);
+ wsRemoteEndpointServer = new WsRemoteEndpointImplServer(socketWrapper, upgradeInfo, webSocketContainer);
wsSession = new WsSession(ep, wsRemoteEndpointServer,
webSocketContainer, handshakeRequest.getRequestURI(),
handshakeRequest.getParameterMap(),
@@ -125,7 +127,7 @@ public class WsHttpUpgradeHandler implements InternalHttpUpgradeHandler {
handshakeRequest.getUserPrincipal(), httpSessionId,
negotiatedExtensions, subProtocol, pathParameters, secure,
serverEndpointConfig);
- wsFrame = new WsFrameServer(socketWrapper, wsSession, transformation,
+ wsFrame = new WsFrameServer(socketWrapper, upgradeInfo, wsSession, transformation,
applicationClassLoader);
// WsFrame adds the necessary final transformations. Copy the
// completed transformation chain to the remote end point.
@@ -141,6 +143,12 @@ public class WsHttpUpgradeHandler implements InternalHttpUpgradeHandler {
@Override
+ public UpgradeInfo getUpgradeInfo() {
+ return upgradeInfo;
+ }
+
+
+ @Override
public SocketState upgradeDispatch(SocketEvent status) {
switch (status) {
case OPEN_READ:
diff --git a/java/org/apache/tomcat/websocket/server/WsRemoteEndpointImplServer.java b/java/org/apache/tomcat/websocket/server/WsRemoteEndpointImplServer.java
index 0fee189..f689920 100644
--- a/java/org/apache/tomcat/websocket/server/WsRemoteEndpointImplServer.java
+++ b/java/org/apache/tomcat/websocket/server/WsRemoteEndpointImplServer.java
@@ -27,6 +27,7 @@ import java.util.concurrent.TimeUnit;
import javax.websocket.SendHandler;
import javax.websocket.SendResult;
+import org.apache.coyote.http11.upgrade.UpgradeInfo;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.net.SocketWrapperBase;
@@ -46,15 +47,17 @@ public class WsRemoteEndpointImplServer extends WsRemoteEndpointImplBase {
private final Log log = LogFactory.getLog(WsRemoteEndpointImplServer.class); // must not be static
private final SocketWrapperBase<?> socketWrapper;
+ private final UpgradeInfo upgradeInfo;
private final WsWriteTimeout wsWriteTimeout;
private volatile SendHandler handler = null;
private volatile ByteBuffer[] buffers = null;
private volatile long timeoutExpiry = -1;
- public WsRemoteEndpointImplServer(SocketWrapperBase<?> socketWrapper,
+ public WsRemoteEndpointImplServer(SocketWrapperBase<?> socketWrapper, UpgradeInfo upgradeInfo,
WsServerContainer serverContainer) {
this.socketWrapper = socketWrapper;
+ this.upgradeInfo = upgradeInfo;
this.wsWriteTimeout = serverContainer.getTimeout();
}
@@ -154,6 +157,13 @@ public class WsRemoteEndpointImplServer extends WsRemoteEndpointImplBase {
}
+ @Override
+ protected void updateStats(long payloadLength) {
+ upgradeInfo.addMsgsSent(1);
+ upgradeInfo.addBytesSent(payloadLength);
+ }
+
+
public void onWritePossible(boolean useDispatch) {
// Note: Unused for async IO
ByteBuffer[] buffers = this.buffers;
diff --git a/test/org/apache/coyote/http11/upgrade/TestUpgrade.java b/test/org/apache/coyote/http11/upgrade/TestUpgrade.java
index c5180d6..2d99506 100644
--- a/test/org/apache/coyote/http11/upgrade/TestUpgrade.java
+++ b/test/org/apache/coyote/http11/upgrade/TestUpgrade.java
@@ -177,6 +177,7 @@ public class TestUpgrade extends TomcatBaseTest {
uc.getWriter().write("GET / HTTP/1.1" + CRLF);
uc.getWriter().write("Host: whatever" + CRLF);
+ uc.getWriter().write("Upgrade: test" + CRLF);
uc.getWriter().write(CRLF);
uc.getWriter().flush();
@@ -209,6 +210,9 @@ public class TestUpgrade extends TomcatBaseTest {
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
+ // In these tests only a single protocol is requested so it is safe
+ // to echo it to the response.
+ resp.setHeader("upgrade", req.getHeader("upgrade"));
req.upgrade(upgradeHandlerClass);
}
}
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 278a0b7..eff1dd0 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -83,8 +83,8 @@
Based on a pull request by willmeck. (markt)
</fix>
<fix>
- Implement a partial fix for <bug>63362</bug> that adds collection of
- request statistics for HTTP/2 requests. (markt)
+ <bug>63362</bug>: Add collection of statistics for HTTP/2, WebSocket and
+ connections upgraded via the HTTP upgrade mechanism. (markt)
</fix>
</changelog>
</subsection>
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org