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

package io.netty.handler.codec.http;

import io.netty.util.NetUtil;
import java.net.InetSocketAddress;
import io.netty.util.internal.ObjectUtil;
import java.nio.charset.UnsupportedCharsetException;
import java.nio.charset.IllegalCharsetNameException;
import io.netty.util.CharsetUtil;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.List;
import java.util.Collection;
import java.util.ArrayList;
import java.net.URI;
import io.netty.util.AsciiString;

public final class HttpUtil
{
    private static final AsciiString CHARSET_EQUALS;
    private static final AsciiString SEMICOLON;
    private static final String COMMA_STRING;
    private static final long TOKEN_CHARS_HIGH = 6341068274398134270L;
    private static final long TOKEN_CHARS_LOW = 288068722172624896L;
    
    private HttpUtil() {
    }
    
    public static boolean isOriginForm(final URI uri) {
        return isOriginForm(uri.toString());
    }
    
    public static boolean isOriginForm(final String uri) {
        return uri.startsWith("/");
    }
    
    public static boolean isAsteriskForm(final URI uri) {
        return isAsteriskForm(uri.toString());
    }
    
    public static boolean isAsteriskForm(final String uri) {
        return "*".equals(uri);
    }
    
    static void validateRequestLineTokens(final HttpMethod method, final String uri) {
        if (method.getClass() != HttpMethod.class && !isEncodingSafeStartLineToken(method.asciiName())) {
            throw new IllegalArgumentException("The HTTP method name contain illegal characters: " + (Object)method.asciiName());
        }
        if (!isEncodingSafeStartLineToken(uri)) {
            throw new IllegalArgumentException("The URI contain illegal characters: " + uri);
        }
    }
    
    public static boolean isEncodingSafeStartLineToken(final CharSequence token) {
        for (int lenBytes = token.length(), i = 0; i < lenBytes; ++i) {
            final char ch = token.charAt(i);
            if (ch <= ' ') {
                switch (ch) {
                    case '\n':
                    case '\r':
                    case ' ': {
                        return false;
                    }
                }
            }
        }
        return true;
    }
    
    public static boolean isKeepAlive(final HttpMessage message) {
        return !message.headers().containsValue(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE, true) && (message.protocolVersion().isKeepAliveDefault() || message.headers().containsValue(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE, true));
    }
    
    public static void setKeepAlive(final HttpMessage message, final boolean keepAlive) {
        setKeepAlive(message.headers(), message.protocolVersion(), keepAlive);
    }
    
    public static void setKeepAlive(final HttpHeaders h, final HttpVersion httpVersion, final boolean keepAlive) {
        if (httpVersion.isKeepAliveDefault()) {
            if (keepAlive) {
                h.remove(HttpHeaderNames.CONNECTION);
            }
            else {
                h.set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
            }
        }
        else if (keepAlive) {
            h.set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
        }
        else {
            h.remove(HttpHeaderNames.CONNECTION);
        }
    }
    
    public static long getContentLength(final HttpMessage message) {
        final String value = message.headers().get(HttpHeaderNames.CONTENT_LENGTH);
        if (value != null) {
            return Long.parseLong(value);
        }
        final long webSocketContentLength = getWebSocketContentLength(message);
        if (webSocketContentLength >= 0L) {
            return webSocketContentLength;
        }
        throw new NumberFormatException("header not found: " + (Object)HttpHeaderNames.CONTENT_LENGTH);
    }
    
    public static long getContentLength(final HttpMessage message, final long defaultValue) {
        final String value = message.headers().get(HttpHeaderNames.CONTENT_LENGTH);
        if (value != null) {
            return Long.parseLong(value);
        }
        final long webSocketContentLength = getWebSocketContentLength(message);
        if (webSocketContentLength >= 0L) {
            return webSocketContentLength;
        }
        return defaultValue;
    }
    
    public static int getContentLength(final HttpMessage message, final int defaultValue) {
        return (int)Math.min(2147483647L, getContentLength(message, (long)defaultValue));
    }
    
    static int getWebSocketContentLength(final HttpMessage message) {
        final HttpHeaders h = message.headers();
        if (message instanceof HttpRequest) {
            final HttpRequest req = (HttpRequest)message;
            if (HttpMethod.GET.equals(req.method()) && h.contains(HttpHeaderNames.SEC_WEBSOCKET_KEY1) && h.contains(HttpHeaderNames.SEC_WEBSOCKET_KEY2)) {
                return 8;
            }
        }
        else if (message instanceof HttpResponse) {
            final HttpResponse res = (HttpResponse)message;
            if (res.status().code() == 101 && h.contains(HttpHeaderNames.SEC_WEBSOCKET_ORIGIN) && h.contains(HttpHeaderNames.SEC_WEBSOCKET_LOCATION)) {
                return 16;
            }
        }
        return -1;
    }
    
    public static void setContentLength(final HttpMessage message, final long length) {
        message.headers().set(HttpHeaderNames.CONTENT_LENGTH, length);
    }
    
    public static boolean isContentLengthSet(final HttpMessage m) {
        return m.headers().contains(HttpHeaderNames.CONTENT_LENGTH);
    }
    
    public static boolean is100ContinueExpected(final HttpMessage message) {
        return isExpectHeaderValid(message) && message.headers().contains(HttpHeaderNames.EXPECT, HttpHeaderValues.CONTINUE, true);
    }
    
    static boolean isUnsupportedExpectation(final HttpMessage message) {
        if (!isExpectHeaderValid(message)) {
            return false;
        }
        final String expectValue = message.headers().get(HttpHeaderNames.EXPECT);
        return expectValue != null && !HttpHeaderValues.CONTINUE.toString().equalsIgnoreCase(expectValue);
    }
    
    private static boolean isExpectHeaderValid(final HttpMessage message) {
        return message instanceof HttpRequest && message.protocolVersion().compareTo(HttpVersion.HTTP_1_1) >= 0;
    }
    
    public static void set100ContinueExpected(final HttpMessage message, final boolean expected) {
        if (expected) {
            message.headers().set(HttpHeaderNames.EXPECT, HttpHeaderValues.CONTINUE);
        }
        else {
            message.headers().remove(HttpHeaderNames.EXPECT);
        }
    }
    
    public static boolean isTransferEncodingChunked(final HttpMessage message) {
        return message.headers().containsValue(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED, true);
    }
    
    public static void setTransferEncodingChunked(final HttpMessage m, final boolean chunked) {
        if (chunked) {
            m.headers().set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED);
            m.headers().remove(HttpHeaderNames.CONTENT_LENGTH);
        }
        else {
            final List<String> encodings = m.headers().getAll(HttpHeaderNames.TRANSFER_ENCODING);
            if (encodings.isEmpty()) {
                return;
            }
            final List<CharSequence> values = new ArrayList<CharSequence>(encodings);
            final Iterator<CharSequence> valuesIt = values.iterator();
            while (valuesIt.hasNext()) {
                final CharSequence value = valuesIt.next();
                if (HttpHeaderValues.CHUNKED.contentEqualsIgnoreCase(value)) {
                    valuesIt.remove();
                }
            }
            if (values.isEmpty()) {
                m.headers().remove(HttpHeaderNames.TRANSFER_ENCODING);
            }
            else {
                m.headers().set(HttpHeaderNames.TRANSFER_ENCODING, values);
            }
        }
    }
    
    public static Charset getCharset(final HttpMessage message) {
        return getCharset(message, CharsetUtil.ISO_8859_1);
    }
    
    public static Charset getCharset(final CharSequence contentTypeValue) {
        if (contentTypeValue != null) {
            return getCharset(contentTypeValue, CharsetUtil.ISO_8859_1);
        }
        return CharsetUtil.ISO_8859_1;
    }
    
    public static Charset getCharset(final HttpMessage message, final Charset defaultCharset) {
        final CharSequence contentTypeValue = message.headers().get(HttpHeaderNames.CONTENT_TYPE);
        if (contentTypeValue != null) {
            return getCharset(contentTypeValue, defaultCharset);
        }
        return defaultCharset;
    }
    
    public static Charset getCharset(final CharSequence contentTypeValue, final Charset defaultCharset) {
        if (contentTypeValue != null) {
            CharSequence charsetRaw = getCharsetAsSequence(contentTypeValue);
            if (charsetRaw != null) {
                if (charsetRaw.length() > 2 && charsetRaw.charAt(0) == '\"' && charsetRaw.charAt(charsetRaw.length() - 1) == '\"') {
                    charsetRaw = charsetRaw.subSequence(1, charsetRaw.length() - 1);
                }
                try {
                    return Charset.forName(charsetRaw.toString());
                }
                catch (final IllegalCharsetNameException | UnsupportedCharsetException ex) {}
            }
        }
        return defaultCharset;
    }
    
    @Deprecated
    public static CharSequence getCharsetAsString(final HttpMessage message) {
        return getCharsetAsSequence(message);
    }
    
    public static CharSequence getCharsetAsSequence(final HttpMessage message) {
        final CharSequence contentTypeValue = message.headers().get(HttpHeaderNames.CONTENT_TYPE);
        if (contentTypeValue != null) {
            return getCharsetAsSequence(contentTypeValue);
        }
        return null;
    }
    
    public static CharSequence getCharsetAsSequence(final CharSequence contentTypeValue) {
        ObjectUtil.checkNotNull(contentTypeValue, "contentTypeValue");
        final int indexOfCharset = AsciiString.indexOfIgnoreCaseAscii(contentTypeValue, HttpUtil.CHARSET_EQUALS, 0);
        if (indexOfCharset == -1) {
            return null;
        }
        final int indexOfEncoding = indexOfCharset + HttpUtil.CHARSET_EQUALS.length();
        if (indexOfEncoding >= contentTypeValue.length()) {
            return null;
        }
        final CharSequence charsetCandidate = contentTypeValue.subSequence(indexOfEncoding, contentTypeValue.length());
        final int indexOfSemicolon = AsciiString.indexOfIgnoreCaseAscii(charsetCandidate, HttpUtil.SEMICOLON, 0);
        if (indexOfSemicolon == -1) {
            return charsetCandidate;
        }
        return charsetCandidate.subSequence(0, indexOfSemicolon);
    }
    
    public static CharSequence getMimeType(final HttpMessage message) {
        final CharSequence contentTypeValue = message.headers().get(HttpHeaderNames.CONTENT_TYPE);
        if (contentTypeValue != null) {
            return getMimeType(contentTypeValue);
        }
        return null;
    }
    
    public static CharSequence getMimeType(final CharSequence contentTypeValue) {
        ObjectUtil.checkNotNull(contentTypeValue, "contentTypeValue");
        final int indexOfSemicolon = AsciiString.indexOfIgnoreCaseAscii(contentTypeValue, HttpUtil.SEMICOLON, 0);
        if (indexOfSemicolon != -1) {
            return contentTypeValue.subSequence(0, indexOfSemicolon);
        }
        return (contentTypeValue.length() > 0) ? contentTypeValue : null;
    }
    
    public static String formatHostnameForHttp(final InetSocketAddress addr) {
        String hostString = NetUtil.getHostname(addr);
        if (NetUtil.isValidIpV6Address(hostString)) {
            if (!addr.isUnresolved()) {
                hostString = NetUtil.toAddressString(addr.getAddress());
            }
            else if (hostString.charAt(0) == '[' && hostString.charAt(hostString.length() - 1) == ']') {
                return hostString;
            }
            return '[' + hostString + ']';
        }
        return hostString;
    }
    
    public static long normalizeAndGetContentLength(final List<? extends CharSequence> contentLengthFields, final boolean isHttp10OrEarlier, final boolean allowDuplicateContentLengths) {
        if (contentLengthFields.isEmpty()) {
            return -1L;
        }
        String firstField = ((CharSequence)contentLengthFields.get(0)).toString();
        final boolean multipleContentLengths = contentLengthFields.size() > 1 || firstField.indexOf(44) >= 0;
        if (multipleContentLengths && !isHttp10OrEarlier) {
            if (!allowDuplicateContentLengths) {
                throw new IllegalArgumentException("Multiple Content-Length values found: " + contentLengthFields);
            }
            String firstValue = null;
            for (final CharSequence field : contentLengthFields) {
                final String[] split;
                final String[] tokens = split = field.toString().split(HttpUtil.COMMA_STRING, -1);
                for (final String token : split) {
                    final String trimmed = token.trim();
                    if (firstValue == null) {
                        firstValue = trimmed;
                    }
                    else if (!trimmed.equals(firstValue)) {
                        throw new IllegalArgumentException("Multiple Content-Length values found: " + contentLengthFields);
                    }
                }
            }
            firstField = firstValue;
        }
        if (firstField.isEmpty() || !Character.isDigit(firstField.charAt(0))) {
            throw new IllegalArgumentException("Content-Length value is not a number: " + firstField);
        }
        try {
            final long value = Long.parseLong(firstField);
            return ObjectUtil.checkPositiveOrZero(value, "Content-Length value");
        }
        catch (final NumberFormatException e) {
            throw new IllegalArgumentException("Content-Length value is not a number: " + firstField, e);
        }
    }
    
    static int validateToken(final CharSequence token) {
        if (token instanceof AsciiString) {
            return validateAsciiStringToken((AsciiString)token);
        }
        return validateCharSequenceToken(token);
    }
    
    private static int validateAsciiStringToken(final AsciiString token) {
        final byte[] array = token.array();
        for (int i = token.arrayOffset(), len = token.arrayOffset() + token.length(); i < len; ++i) {
            if (!isValidTokenChar(array[i])) {
                return i - token.arrayOffset();
            }
        }
        return -1;
    }
    
    private static int validateCharSequenceToken(final CharSequence token) {
        for (int i = 0, len = token.length(); i < len; ++i) {
            final byte value = (byte)token.charAt(i);
            if (!isValidTokenChar(value)) {
                return i;
            }
        }
        return -1;
    }
    
    private static boolean isValidTokenChar(final byte bit) {
        if (bit < 0) {
            return false;
        }
        if (bit < 64) {
            return 0x0L != (0x3FF6CFA00000000L & 1L << bit);
        }
        return 0x0L != (0x57FFFFFFC7FFFFFEL & 1L << bit - 64);
    }
    
    static {
        CHARSET_EQUALS = AsciiString.of((Object)HttpHeaderValues.CHARSET + "=");
        SEMICOLON = AsciiString.cached(";");
        COMMA_STRING = String.valueOf(',');
    }
}
