// 
// Decompiled by Procyon v0.6.0
// 

package io.netty.handler.codec.http3;

import io.netty.util.AsciiString;
import io.netty.handler.codec.Headers;
import io.netty.util.concurrent.Future;
import io.netty.handler.codec.quic.QuicStreamChannel;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.internal.StringUtil;
import java.util.function.BooleanSupplier;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;

final class Http3RequestStreamValidationUtils
{
    static final long CONTENT_LENGTH_NOT_MODIFIED = -1L;
    static final long INVALID_FRAME_READ = -2L;
    
    private Http3RequestStreamValidationUtils() {
    }
    
    static boolean validateClientWrite(final Http3RequestStreamFrame frame, final ChannelPromise promise, final ChannelHandlerContext ctx, final BooleanSupplier goAwayReceivedSupplier, final Http3RequestStreamCodecState encodeState) {
        if (goAwayReceivedSupplier.getAsBoolean() && !encodeState.started()) {
            final String type = StringUtil.simpleClassName(frame);
            ReferenceCountUtil.release(frame);
            promise.setFailure((Throwable)new Http3Exception(Http3ErrorCode.H3_FRAME_UNEXPECTED, "Frame of type " + type + " unexpected as we received a GOAWAY already."));
            ctx.close();
            return false;
        }
        if (frame instanceof Http3PushPromiseFrame) {
            Http3FrameValidationUtils.frameTypeUnexpected(promise, frame);
            return false;
        }
        return true;
    }
    
    static long validateHeaderFrameRead(final Http3HeadersFrame headersFrame, final ChannelHandlerContext ctx, final Http3RequestStreamCodecState decodeState) {
        if (((Headers<AsciiString, V, T>)headersFrame.headers()).contains(HttpHeaderNames.CONNECTION)) {
            headerUnexpected(ctx, headersFrame, "connection header included");
            return -2L;
        }
        final CharSequence value = ((Headers<AsciiString, CharSequence, T>)headersFrame.headers()).get(HttpHeaderNames.TE);
        if (value != null && !HttpHeaderValues.TRAILERS.equals(value)) {
            headerUnexpected(ctx, headersFrame, "te header field included with invalid value: " + (Object)value);
            return -2L;
        }
        if (decodeState.receivedFinalHeaders()) {
            final long length = HttpUtil.normalizeAndGetContentLength(((Headers<AsciiString, ? extends CharSequence, T>)headersFrame.headers()).getAll(HttpHeaderNames.CONTENT_LENGTH), false, true);
            if (length != -1L) {
                ((Headers<AsciiString, Object, Headers>)headersFrame.headers()).setLong(HttpHeaderNames.CONTENT_LENGTH, length);
            }
            return length;
        }
        return -1L;
    }
    
    static long validateDataFrameRead(final Http3DataFrame dataFrame, final ChannelHandlerContext ctx, final long expectedLength, final long seenLength, final boolean clientHeadRequest) {
        try {
            return verifyContentLength(dataFrame.content().readableBytes(), expectedLength, seenLength, false, clientHeadRequest);
        }
        catch (final Http3Exception e) {
            ReferenceCountUtil.release(dataFrame);
            failStream(ctx, e);
            return -2L;
        }
    }
    
    static boolean validateOnStreamClosure(final ChannelHandlerContext ctx, final long expectedLength, final long seenLength, final boolean clientHeadRequest) {
        try {
            verifyContentLength(0, expectedLength, seenLength, true, clientHeadRequest);
            return true;
        }
        catch (final Http3Exception e) {
            ctx.fireExceptionCaught((Throwable)e);
            Http3CodecUtils.streamError(ctx, e.errorCode());
            return false;
        }
    }
    
    static void sendStreamAbandonedIfRequired(final ChannelHandlerContext ctx, final QpackAttributes qpackAttributes, final QpackDecoder qpackDecoder, final Http3RequestStreamCodecState decodeState) {
        if (!qpackAttributes.dynamicTableDisabled() && !decodeState.terminated()) {
            final long streamId = ((QuicStreamChannel)ctx.channel()).streamId();
            if (qpackAttributes.decoderStreamAvailable()) {
                qpackDecoder.streamAbandoned(qpackAttributes.decoderStream(), streamId);
            }
            else {
                qpackAttributes.whenDecoderStreamAvailable(future -> {
                    if (future.isSuccess()) {
                        qpackDecoder.streamAbandoned(qpackAttributes.decoderStream(), streamId);
                    }
                });
            }
        }
    }
    
    private static void headerUnexpected(final ChannelHandlerContext ctx, final Http3RequestStreamFrame frame, final String msg) {
        ReferenceCountUtil.release(frame);
        failStream(ctx, new Http3Exception(Http3ErrorCode.H3_MESSAGE_ERROR, msg));
    }
    
    private static void failStream(final ChannelHandlerContext ctx, final Http3Exception cause) {
        ctx.fireExceptionCaught((Throwable)cause);
        Http3CodecUtils.streamError(ctx, cause.errorCode());
    }
    
    private static long verifyContentLength(final int length, final long expectedLength, long seenLength, final boolean end, final boolean clientHeadRequest) throws Http3Exception {
        seenLength += length;
        if (expectedLength != -1L && (seenLength > expectedLength || (!clientHeadRequest && end && seenLength != expectedLength))) {
            throw new Http3Exception(Http3ErrorCode.H3_MESSAGE_ERROR, "Expected content-length " + expectedLength + " != " + seenLength + ".");
        }
        return seenLength;
    }
}
