This is an automated email from the ASF dual-hosted git repository. markt pushed a commit to branch 8.5.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/8.5.x by this push: new b709415 Complete fix for BZ 63362. Collect stats for h2, websocket and upgrade b709415 is described below commit b709415efb391d6f6f3aa4423494a2dd3610c59d 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 | 26 ----- java/org/apache/coyote/LocalStrings.properties | 1 - java/org/apache/coyote/UpgradeToken.java | 9 +- .../coyote/http11/AbstractHttp11Protocol.java | 100 +++++++++++++++-- java/org/apache/coyote/http11/Http11Processor.java | 2 +- .../apache/coyote/http11/LocalStrings.properties | 2 + .../http11/upgrade/InternalHttpUpgradeHandler.java | 2 + .../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 | 46 ++++---- .../apache/coyote/http2/Http2UpgradeHandler.java | 8 ++ .../apache/coyote/http2/LocalStrings.properties | 2 + java/org/apache/coyote/http2/StreamProcessor.java | 6 +- java/org/apache/coyote/mbeans-descriptors.xml | 92 ++++++++++++++++ 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 + .../http11/upgrade/TestUpgradeInternalHandler.java | 6 +- webapps/docs/changelog.xml | 4 +- 27 files changed, 562 insertions(+), 82 deletions(-) diff --git a/java/org/apache/catalina/connector/Request.java b/java/org/apache/catalina/connector/Request.java index 1d2c341..67be9ce 100644 --- a/java/org/apache/catalina/connector/Request.java +++ b/java/org/apache/catalina/connector/Request.java @@ -2046,7 +2046,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 dd1359b..247a5b0 100644 --- a/java/org/apache/coyote/AbstractProtocol.java +++ b/java/org/apache/coyote/AbstractProtocol.java @@ -35,7 +35,6 @@ import javax.management.ObjectName; import javax.servlet.http.HttpUpgradeHandler; import javax.servlet.http.WebConnection; -import org.apache.coyote.http2.Http2Protocol; import org.apache.juli.logging.Log; import org.apache.tomcat.InstanceManager; import org.apache.tomcat.util.ExceptionUtils; @@ -589,17 +588,6 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler, endpoint.setDomain(domain); endpoint.init(); - - UpgradeProtocol[] upgradeProtocols = findUpgradeProtocols(); - for (UpgradeProtocol upgradeProtocol : upgradeProtocols) { - // Need to do this as we can't add methods to UpgradeProtocol - // without running the risk of breaking a custom upgrade protocol - if (upgradeProtocol instanceof Http2Protocol) { - // Implementation note: Failure of one upgrade protocol fails the - // whole Connector - ((Http2Protocol) upgradeProtocol).init(); - } - } } @@ -664,20 +652,6 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler, getLog().info(sm.getString("abstractProtocolHandler.destroy", getName())); } - UpgradeProtocol[] upgradeProtocols = findUpgradeProtocols(); - for (UpgradeProtocol upgradeProtocol : upgradeProtocols) { - try { - // Need to do this as we can't add methods to UpgradeProtocol - // without running the risk of breaking a custom upgrade protocol - if (upgradeProtocol instanceof Http2Protocol) { - ((Http2Protocol) 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 77911cc..1aeb314 100644 --- a/java/org/apache/coyote/LocalStrings.properties +++ b/java/org/apache/coyote/LocalStrings.properties @@ -35,7 +35,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/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 d28e4fc..30f5957 100644 --- a/java/org/apache/coyote/http11/AbstractHttp11Protocol.java +++ b/java/org/apache/coyote/http11/AbstractHttp11Protocol.java @@ -26,6 +26,8 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import javax.management.ObjectInstance; +import javax.management.ObjectName; import javax.servlet.http.HttpUpgradeHandler; import org.apache.coyote.AbstractProtocol; @@ -37,10 +39,13 @@ 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.coyote.http2.Http2Protocol; 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,33 @@ 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) { + if (upgradeProtocol instanceof Http2Protocol) { + ((Http2Protocol) 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(); } @@ -535,10 +567,6 @@ public abstract class AbstractHttp11Protocol<S> extends AbstractProtocol<S> { } } } - - if (upgradeProtocol instanceof Http2Protocol) { - ((Http2Protocol) upgradeProtocol).setHttp11Protocol(this); - } } @Override public UpgradeProtocol getNegotiatedProtocol(String negotiatedName) { @@ -550,6 +578,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 @@ -1014,9 +1102,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 5d27a8d..68beb91 100644 --- a/java/org/apache/coyote/http11/Http11Processor.java +++ b/java/org/apache/coyote/http11/Http11Processor.java @@ -579,7 +579,7 @@ public class Http11Processor extends AbstractProcessor { InternalHttpUpgradeHandler upgradeHandler = upgradeProtocol.getInternalUpgradeHandler( 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 d547b1d..f545ddf 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 426b1bd..608a91c 100644 --- a/java/org/apache/coyote/http11/upgrade/InternalHttpUpgradeHandler.java +++ b/java/org/apache/coyote/http11/upgrade/InternalHttpUpgradeHandler.java @@ -39,4 +39,6 @@ public interface InternalHttpUpgradeHandler extends HttpUpgradeHandler { void setSslSupport(SSLSupport sslSupport); void pause(); + + UpgradeInfo getUpgradeInfo(); } \ 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 f0f5460..ac39094 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); + } } @@ -83,6 +87,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 79f9314..b8d3b0a 100644 --- a/java/org/apache/coyote/http2/Http2Protocol.java +++ b/java/org/apache/coyote/http2/Http2Protocol.java @@ -41,12 +41,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; @@ -100,7 +106,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) { @@ -123,8 +128,9 @@ public class Http2Protocol implements UpgradeProtocol { @Override public Processor getProcessor(SocketWrapperBase<?> socketWrapper, Adapter adapter) { + String upgradeProtocol = getUpgradeProtocolName(); UpgradeProcessorInternal processor = new UpgradeProcessorInternal(socketWrapper, - new UpgradeToken(getInternalUpgradeHandler(adapter, null), null, null)); + new UpgradeToken(getInternalUpgradeHandler(adapter, null), null, null, upgradeProtocol), null); return processor; } @@ -434,36 +440,26 @@ public class Http2Protocol implements UpgradeProtocol { 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); + } } - 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; } } - 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/Http2UpgradeHandler.java b/java/org/apache/coyote/http2/Http2UpgradeHandler.java index 458b112..d80e26a 100644 --- a/java/org/apache/coyote/http2/Http2UpgradeHandler.java +++ b/java/org/apache/coyote/http2/Http2UpgradeHandler.java @@ -42,6 +42,7 @@ import org.apache.coyote.Adapter; import org.apache.coyote.ProtocolException; import org.apache.coyote.Request; import org.apache.coyote.http11.upgrade.InternalHttpUpgradeHandler; +import org.apache.coyote.http11.upgrade.UpgradeInfo; import org.apache.coyote.http2.HpackDecoder.HeaderEmitter; import org.apache.coyote.http2.HpackEncoder.State; import org.apache.coyote.http2.Http2Parser.Input; @@ -484,6 +485,13 @@ public class Http2UpgradeHandler extends AbstractStream implements InternalHttpU } + @Override + public UpgradeInfo getUpgradeInfo() { + // Uses RequestInfo rather than UpgradeInfo + return null; + } + + private void checkPauseState() throws IOException { if (connectionState.get() == ConnectionState.PAUSING) { if (pausedNanoTime + pingManager.getRoundTripTimeNano() < System.nanoTime()) { diff --git a/java/org/apache/coyote/http2/LocalStrings.properties b/java/org/apache/coyote/http2/LocalStrings.properties index 2c17966..c1e8c0a 100644 --- a/java/org/apache/coyote/http2/LocalStrings.properties +++ b/java/org/apache/coyote/http2/LocalStrings.properties @@ -71,6 +71,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 119be9e..c8fc304 100644 --- a/java/org/apache/coyote/http2/StreamProcessor.java +++ b/java/org/apache/coyote/http2/StreamProcessor.java @@ -26,6 +26,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; @@ -337,7 +338,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 new file mode 100644 index 0000000..e23b15b --- /dev/null +++ b/java/org/apache/coyote/mbeans-descriptors.xml @@ -0,0 +1,92 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<!DOCTYPE mbeans-descriptors PUBLIC + "-//Apache Software Foundation//DTD Model MBeans Configuration File" + "http://jakarta.apache.org/commons/dtds/mbeans-descriptors.dtd"> +<mbeans-descriptors> + + <mbean name="RequestGroupInfo" + description="Runtime information of a group of requests" + domain="Catalina" + group="Connector" + type="org.apache.coyote.RequestGroupInfo"> + + <attribute name="maxTime" + description="Maximum time to process a request" + type="long" + writeable="false"/> + + <attribute name="processingTime" + description="Total time to process the requests" + type="long" + writeable="false"/> + + <attribute name="requestCount" + description="Number of requests processed" + type="int" + writeable="false"/> + + <attribute name="errorCount" + description="Number of errors" + type="int" + writeable="false"/> + + <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"/> + + <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 b370123..360c415 100644 --- a/java/org/apache/tomcat/websocket/server/WsRemoteEndpointImplServer.java +++ b/java/org/apache/tomcat/websocket/server/WsRemoteEndpointImplServer.java @@ -25,6 +25,7 @@ import java.util.concurrent.Executor; 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.AbstractEndpoint; @@ -44,15 +45,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(); } @@ -102,6 +105,13 @@ public class WsRemoteEndpointImplServer extends WsRemoteEndpointImplBase { } + @Override + protected void updateStats(long payloadLength) { + upgradeInfo.addMsgsSent(1); + upgradeInfo.addBytesSent(payloadLength); + } + + public void onWritePossible(boolean useDispatch) { ByteBuffer[] buffers = this.buffers; if (buffers == null) { 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/test/org/apache/coyote/http11/upgrade/TestUpgradeInternalHandler.java b/test/org/apache/coyote/http11/upgrade/TestUpgradeInternalHandler.java index 27bce41..71b6d27 100644 --- a/test/org/apache/coyote/http11/upgrade/TestUpgradeInternalHandler.java +++ b/test/org/apache/coyote/http11/upgrade/TestUpgradeInternalHandler.java @@ -282,6 +282,10 @@ public class TestUpgradeInternalHandler extends TomcatBaseTest { public void setSslSupport(SSLSupport sslSupport) { // NO-OP } - } + @Override + public UpgradeInfo getUpgradeInfo() { + return null; + } + } } diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index 7fabb3b..b02f143 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -82,8 +82,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> </subseciton> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org