/* * Copyright 2016 LINE Corporation * * LINE Corporation 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 com.linecorp.armeria.internal.http; import java.nio.charset.StandardCharsets; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import io.netty.handler.codec.http2.Http2ConnectionAdapter; import io.netty.handler.codec.http2.Http2Error; import io.netty.handler.codec.http2.Http2Stream; /** * A {@link Http2ConnectionAdapter} that logs the received GOAWAY frames and makes sure disconnection. */ public class Http2GoAwayListener extends Http2ConnectionAdapter { private static final Logger logger = LoggerFactory.getLogger(Http2GoAwayListener.class); private final Channel ch; private boolean goAwaySent; public Http2GoAwayListener(Channel ch) { this.ch = ch; } @Override public void onGoAwaySent(int lastStreamId, long errorCode, ByteBuf debugData) { goAwaySent = true; onGoAway("Sent", lastStreamId, errorCode, debugData); } @Override public void onGoAwayReceived(int lastStreamId, long errorCode, ByteBuf debugData) { onGoAway("Received", lastStreamId, errorCode, debugData); // Send a GOAWAY back to the peer and close the connection gracefully if we did not send GOAWAY yet. // This will make sure that the connection is always closed after receiving GOAWAY, // because otherwise we have to wait until the peer who sent GOAWAY to us closes the connection. if (!goAwaySent) { ch.close(); } } private void onGoAway(String sentOrReceived, int lastStreamId, long errorCode, ByteBuf debugData) { if (errorCode != Http2Error.NO_ERROR.code()) { if (logger.isWarnEnabled()) { logger.warn("{} {} a GOAWAY frame: lastStreamId={}, errorCode={}, debugData=\"{}\"", ch, sentOrReceived, lastStreamId, errorStr(errorCode), debugData.toString(StandardCharsets.UTF_8)); } } else { if (logger.isDebugEnabled()) { logger.debug("{} {} a GOAWAY frame: lastStreamId={}, errorCode=NO_ERROR", ch, sentOrReceived, lastStreamId); } } } private static String errorStr(long errorCode) { final Http2Error error = Http2Error.valueOf(errorCode); return error != null ? error.toString() + '(' + errorCode + ')' : "UNKNOWN(" + errorCode + ')'; } @Override public void onStreamRemoved(Http2Stream stream) { if (stream.id() == 1) { logger.debug("{} HTTP/2 upgrade stream removed: {}", ch, stream.state()); } } }