/
githubmirror
/
galahad
Обзор
Документация
Войти
/
githubmirror
/
galahad
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/java.net.http/share/classes/jdk/internal/net/http/Http2TerminationCause.java
281 строка
10 KB
Jaikiran Pai
8326498: java.net.http.HttpClient connection leak using http/2
25 ноя 2025, 14:13
25 ноя 2025, 14:13
c19b129
Код
Авторство
О чём код?
/* * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package jdk.internal.net.http; import java.io.IOException; import java.net.ProtocolException; import java.util.Objects; import jdk.internal.net.http.common.Utils; import jdk.internal.net.http.frame.ErrorFrame; /** * Termination cause for an {@linkplain Http2Connection HTTP/2 connection} */ public abstract sealed class Http2TerminationCause { private String logMsg; private String peerVisibleReason; private final int closeCode; private final Throwable originalCause; private final IOException reportedCause; private Http2TerminationCause(final int closeCode, final Throwable closeCause) { this.closeCode = closeCode; this.originalCause = closeCause; if (closeCause != null) { this.logMsg = closeCause.toString(); } this.reportedCause = toReportedCause(this.originalCause, this.logMsg); } private Http2TerminationCause(final int closeCode, final String loggedAs) { this.closeCode = closeCode; this.originalCause = null; this.logMsg = loggedAs; this.reportedCause = toReportedCause(null, this.logMsg); } /** * Returns the error code (specified for HTTP/2 ErrorFrame) that caused the * connection termination. */ public final int getCloseCode() { return this.closeCode; } /** * Returns the {@link IOException} that is considered the cause of the connection termination. * Even a {@linkplain #isAbnormalClose() normal} termination will have * an {@code IOException} associated with it, so this method will always return a non-null instance. */ public final IOException getCloseCause() { return this.reportedCause; } /** * Returns {@code true} if the connection was terminated due to some exception. {@code false} * otherwise. * A normal connection termination (for example, the connection idle timing out locally) * is not considered as an abnormal termination and this method returns {@code false} for * such cases. */ public abstract boolean isAbnormalClose(); /** * Returns the connection termination cause, represented as a string. Unlike the * {@linkplain #getPeerVisibleReason() peer-visible reason}, this log message will not be * sent across to the peer and it is thus allowed to include additional details that might * help debugging a connection termination. */ public final String getLogMsg() { return logMsg; } /** * Returns the connection termination cause, represented as a string. This represents the * "debugData" that is sent to the peer in a * {@linkplain jdk.internal.net.http.frame.GoAwayFrame GOAWAY frame}. */ public final String getPeerVisibleReason() { return this.peerVisibleReason; } /** * Sets the connection termination cause, represented as a string, which will be sent * to the peer in a {@linkplain jdk.internal.net.http.frame.GoAwayFrame GOAWAY frame}. * Unlike the {@link #getLogMsg() log message}, * it is expected that this peer-visible reason will not contain anything that is not meant * to be viewed by the peer. */ protected final void setPeerVisibleReason(final String reasonPhrase) { this.peerVisibleReason = reasonPhrase; } /** * Returns a connection termination cause that represents an * {@linkplain #isAbnormalClose() abnormal} termination due to the given {@code cause}. * * @param cause the termination cause, cannot be null. */ public static Http2TerminationCause forException(final Throwable cause) { Objects.requireNonNull(cause); if (cause instanceof ProtocolException pe) { return new ProtocolError(pe); } return new InternalError(cause); } /** * Returns a connection termination cause that represents a * {@linkplain #isAbnormalClose() normal} termination. */ public static Http2TerminationCause noErrorTermination() { return NoError.INSTANCE; } /** * Returns a connection termination cause that represents a * {@linkplain #isAbnormalClose() normal} termination due to the connection * being idle. */ public static Http2TerminationCause idleTimedOut() { return NoError.IDLE_TIMED_OUT; } /** * Returns a connection termination cause that represents an * {@linkplain #isAbnormalClose() abnormal} termination due to the given {@code errorCode}. * Although this method does no checks for the {@code errorCode}, it is expected to be one * of the error codes specified by the HTTP/2 RFC for the ErrorFrame. * * @param errorCode the error code * @param loggedAs optional log message to be associated with this termination cause */ public static Http2TerminationCause forH2Error(final int errorCode, final String loggedAs) { if (errorCode == ErrorFrame.PROTOCOL_ERROR) { return new ProtocolError(loggedAs); } else if (errorCode == ErrorFrame.FLOW_CONTROL_ERROR) { // we treat flow control error as a protocol error currently return new ProtocolError(loggedAs, true); } return new H2StandardError(errorCode, loggedAs); } private static IOException toReportedCause(final Throwable original, final String fallbackExceptionMsg) { if (original == null) { return fallbackExceptionMsg == null ? new IOException("connection terminated") : new IOException(fallbackExceptionMsg); } else if (original instanceof IOException ioe) { return ioe; } else { return Utils.toIOException(original); } } private static final class NoError extends Http2TerminationCause { private static final IOException NO_ERROR_MARKER = new IOException("HTTP/2 connection closed normally - no error"); private static final IOException NO_ERROR_IDLE_TIMED_OUT_MARKER = new IOException("HTTP/2 connection idle timed out - no error"); static { // remove the stacktrace from the marker exception instances NO_ERROR_MARKER.setStackTrace(new StackTraceElement[0]); NO_ERROR_IDLE_TIMED_OUT_MARKER.setStackTrace(new StackTraceElement[0]); } private static final NoError INSTANCE = new NoError(false); private static final NoError IDLE_TIMED_OUT = new NoError(true); private final boolean idleTimedOut; private NoError(final boolean idleTimedOut) { super(ErrorFrame.NO_ERROR, idleTimedOut ? NO_ERROR_IDLE_TIMED_OUT_MARKER : NO_ERROR_MARKER); this.idleTimedOut = idleTimedOut; setPeerVisibleReason(idleTimedOut ? "idle timed out" : "no error"); } @Override public boolean isAbnormalClose() { return false; } @Override public String toString() { return this.idleTimedOut ? "No error - idle timed out" : "No error - normal termination"; } } private static sealed class H2StandardError extends Http2TerminationCause { private H2StandardError(final int errCode, final String msg) { super(errCode, msg); setPeerVisibleReason(ErrorFrame.stringForCode(errCode)); } private H2StandardError(final int errCode, final Throwable cause) { super(errCode, cause); setPeerVisibleReason(ErrorFrame.stringForCode(errCode)); } @Override public boolean isAbnormalClose() { return getCloseCode() != ErrorFrame.NO_ERROR; } @Override public String toString() { return ErrorFrame.stringForCode(this.getCloseCode()); } } private static final class ProtocolError extends H2StandardError { private ProtocolError(final String msg) { this(msg, false); } private ProtocolError(final String msg, final boolean flowControlError) { super(flowControlError ? ErrorFrame.FLOW_CONTROL_ERROR : ErrorFrame.PROTOCOL_ERROR, new ProtocolException(msg)); } private ProtocolError(final ProtocolException pe) { super(ErrorFrame.PROTOCOL_ERROR, pe); } @Override public boolean isAbnormalClose() { return true; } @Override public String toString() { return "Protocol error - " + this.getLogMsg(); } } private static final class InternalError extends Http2TerminationCause { private InternalError(final Throwable cause) { super(ErrorFrame.INTERNAL_ERROR, cause); } @Override public boolean isAbnormalClose() { return true; } @Override public String toString() { return "Internal error - " + this.getLogMsg(); } } }