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

package io.netty.handler.codec.http;

import io.netty.handler.codec.PrematureChannelClosureException;
import io.netty.buffer.ByteBuf;
import java.util.List;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import java.util.ArrayDeque;
import java.util.concurrent.atomic.AtomicLong;
import java.util.Queue;
import io.netty.channel.CombinedChannelDuplexHandler;

public final class HttpClientCodec extends CombinedChannelDuplexHandler<HttpResponseDecoder, HttpRequestEncoder> implements HttpClientUpgradeHandler.SourceCodec
{
    public static final boolean DEFAULT_FAIL_ON_MISSING_RESPONSE = false;
    public static final boolean DEFAULT_PARSE_HTTP_AFTER_CONNECT_REQUEST = false;
    private final Queue<HttpMethod> queue;
    private final boolean parseHttpAfterConnectRequest;
    private boolean done;
    private final AtomicLong requestResponseCounter;
    private final boolean failOnMissingResponse;
    
    public HttpClientCodec() {
        this(new HttpDecoderConfig(), false, false);
    }
    
    public HttpClientCodec(final int maxInitialLineLength, final int maxHeaderSize, final int maxChunkSize) {
        this(new HttpDecoderConfig().setMaxInitialLineLength(maxInitialLineLength).setMaxHeaderSize(maxHeaderSize).setMaxChunkSize(maxChunkSize), false, false);
    }
    
    public HttpClientCodec(final int maxInitialLineLength, final int maxHeaderSize, final int maxChunkSize, final boolean failOnMissingResponse) {
        this(new HttpDecoderConfig().setMaxInitialLineLength(maxInitialLineLength).setMaxHeaderSize(maxHeaderSize).setMaxChunkSize(maxChunkSize), false, failOnMissingResponse);
    }
    
    @Deprecated
    public HttpClientCodec(final int maxInitialLineLength, final int maxHeaderSize, final int maxChunkSize, final boolean failOnMissingResponse, final boolean validateHeaders) {
        this(new HttpDecoderConfig().setMaxInitialLineLength(maxInitialLineLength).setMaxHeaderSize(maxHeaderSize).setMaxChunkSize(maxChunkSize).setValidateHeaders(validateHeaders), false, failOnMissingResponse);
    }
    
    @Deprecated
    public HttpClientCodec(final int maxInitialLineLength, final int maxHeaderSize, final int maxChunkSize, final boolean failOnMissingResponse, final boolean validateHeaders, final boolean parseHttpAfterConnectRequest) {
        this(new HttpDecoderConfig().setMaxInitialLineLength(maxInitialLineLength).setMaxHeaderSize(maxHeaderSize).setMaxChunkSize(maxChunkSize).setValidateHeaders(validateHeaders), parseHttpAfterConnectRequest, failOnMissingResponse);
    }
    
    @Deprecated
    public HttpClientCodec(final int maxInitialLineLength, final int maxHeaderSize, final int maxChunkSize, final boolean failOnMissingResponse, final boolean validateHeaders, final int initialBufferSize) {
        this(new HttpDecoderConfig().setMaxInitialLineLength(maxInitialLineLength).setMaxHeaderSize(maxHeaderSize).setMaxChunkSize(maxChunkSize).setValidateHeaders(validateHeaders).setInitialBufferSize(initialBufferSize), false, failOnMissingResponse);
    }
    
    @Deprecated
    public HttpClientCodec(final int maxInitialLineLength, final int maxHeaderSize, final int maxChunkSize, final boolean failOnMissingResponse, final boolean validateHeaders, final int initialBufferSize, final boolean parseHttpAfterConnectRequest) {
        this(new HttpDecoderConfig().setMaxInitialLineLength(maxInitialLineLength).setMaxHeaderSize(maxHeaderSize).setMaxChunkSize(maxChunkSize).setValidateHeaders(validateHeaders).setInitialBufferSize(initialBufferSize), parseHttpAfterConnectRequest, failOnMissingResponse);
    }
    
    @Deprecated
    public HttpClientCodec(final int maxInitialLineLength, final int maxHeaderSize, final int maxChunkSize, final boolean failOnMissingResponse, final boolean validateHeaders, final int initialBufferSize, final boolean parseHttpAfterConnectRequest, final boolean allowDuplicateContentLengths) {
        this(new HttpDecoderConfig().setMaxInitialLineLength(maxInitialLineLength).setMaxHeaderSize(maxHeaderSize).setMaxChunkSize(maxChunkSize).setValidateHeaders(validateHeaders).setInitialBufferSize(initialBufferSize).setAllowDuplicateContentLengths(allowDuplicateContentLengths), parseHttpAfterConnectRequest, failOnMissingResponse);
    }
    
    @Deprecated
    public HttpClientCodec(final int maxInitialLineLength, final int maxHeaderSize, final int maxChunkSize, final boolean failOnMissingResponse, final boolean validateHeaders, final int initialBufferSize, final boolean parseHttpAfterConnectRequest, final boolean allowDuplicateContentLengths, final boolean allowPartialChunks) {
        this(new HttpDecoderConfig().setMaxInitialLineLength(maxInitialLineLength).setMaxHeaderSize(maxHeaderSize).setMaxChunkSize(maxChunkSize).setValidateHeaders(validateHeaders).setInitialBufferSize(initialBufferSize).setAllowDuplicateContentLengths(allowDuplicateContentLengths).setAllowPartialChunks(allowPartialChunks), parseHttpAfterConnectRequest, failOnMissingResponse);
    }
    
    public HttpClientCodec(final HttpDecoderConfig config, final boolean parseHttpAfterConnectRequest, final boolean failOnMissingResponse) {
        this.queue = new ArrayDeque<HttpMethod>();
        this.requestResponseCounter = new AtomicLong();
        ((CombinedChannelDuplexHandler<Decoder, Encoder>)this).init(new Decoder(config), new Encoder());
        this.parseHttpAfterConnectRequest = parseHttpAfterConnectRequest;
        this.failOnMissingResponse = failOnMissingResponse;
    }
    
    @Override
    public void prepareUpgradeFrom(final ChannelHandlerContext ctx) {
        ((CombinedChannelDuplexHandler<I, Encoder>)this).outboundHandler().upgraded = true;
    }
    
    @Override
    public void upgradeFrom(final ChannelHandlerContext ctx) {
        final ChannelPipeline p = ctx.pipeline();
        p.remove(this);
    }
    
    public void setSingleDecode(final boolean singleDecode) {
        ((CombinedChannelDuplexHandler<HttpResponseDecoder, O>)this).inboundHandler().setSingleDecode(singleDecode);
    }
    
    public boolean isSingleDecode() {
        return ((CombinedChannelDuplexHandler<HttpResponseDecoder, O>)this).inboundHandler().isSingleDecode();
    }
    
    private final class Encoder extends HttpRequestEncoder
    {
        boolean upgraded;
        
        @Override
        protected void encode(final ChannelHandlerContext ctx, final Object msg, final List<Object> out) throws Exception {
            if (this.upgraded) {
                out.add(msg);
                return;
            }
            if (msg instanceof HttpRequest) {
                HttpClientCodec.this.queue.offer(((HttpRequest)msg).method());
            }
            super.encode(ctx, msg, out);
            if (HttpClientCodec.this.failOnMissingResponse && !HttpClientCodec.this.done && msg instanceof LastHttpContent) {
                HttpClientCodec.this.requestResponseCounter.incrementAndGet();
            }
        }
    }
    
    private final class Decoder extends HttpResponseDecoder
    {
        Decoder(final HttpDecoderConfig config) {
            super(config);
        }
        
        @Override
        protected void decode(final ChannelHandlerContext ctx, final ByteBuf buffer, final List<Object> out) throws Exception {
            if (HttpClientCodec.this.done) {
                final int readable = this.actualReadableBytes();
                if (readable == 0) {
                    return;
                }
                out.add(buffer.readBytes(readable));
            }
            else {
                final int oldSize = out.size();
                super.decode(ctx, buffer, out);
                if (HttpClientCodec.this.failOnMissingResponse) {
                    for (int size = out.size(), i = oldSize; i < size; ++i) {
                        this.decrement(out.get(i));
                    }
                }
            }
        }
        
        private void decrement(final Object msg) {
            if (msg == null) {
                return;
            }
            if (msg instanceof LastHttpContent) {
                HttpClientCodec.this.requestResponseCounter.decrementAndGet();
            }
        }
        
        @Override
        protected boolean isContentAlwaysEmpty(final HttpMessage msg) {
            final HttpMethod method = HttpClientCodec.this.queue.poll();
            final HttpResponseStatus status = ((HttpResponse)msg).status();
            final HttpStatusClass statusClass = status.codeClass();
            final int statusCode = status.code();
            if (statusClass == HttpStatusClass.INFORMATIONAL) {
                return super.isContentAlwaysEmpty(msg);
            }
            if (method != null) {
                final char firstChar = method.name().charAt(0);
                switch (firstChar) {
                    case 'H': {
                        if (HttpMethod.HEAD.equals(method)) {
                            return true;
                        }
                        break;
                    }
                    case 'C': {
                        if (statusCode == 200 && HttpMethod.CONNECT.equals(method)) {
                            if (!HttpClientCodec.this.parseHttpAfterConnectRequest) {
                                HttpClientCodec.this.done = true;
                                HttpClientCodec.this.queue.clear();
                            }
                            return true;
                        }
                        break;
                    }
                }
            }
            return super.isContentAlwaysEmpty(msg);
        }
        
        @Override
        public void channelInactive(final ChannelHandlerContext ctx) throws Exception {
            super.channelInactive(ctx);
            if (HttpClientCodec.this.failOnMissingResponse) {
                final long missingResponses = HttpClientCodec.this.requestResponseCounter.get();
                if (missingResponses > 0L) {
                    ctx.fireExceptionCaught((Throwable)new PrematureChannelClosureException("channel gone inactive with " + missingResponses + " missing response(s)"));
                }
            }
        }
    }
}
