Instruction:
作为 Java 安全专家，检测此代码中的安全漏洞：
## 内容1
### 文件路径：
core/src/main/java/com/linecorp/armeria/common/HttpHeaderNames.java
### 代码内容：
package com.linecorp.armeria.common;

import static java.util.Objects.requireNonNull;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Map;

import com.google.common.base.Ascii;
import com.google.common.collect.ImmutableMap;

import io.netty.util.AsciiString;

/**
 * Contains constant definitions for the HTTP header field names.
 *
 *
All header names in this class are defined in lowercase to support HTTP/2 requirements while
 * also not violating HTTP/1 requirements.

 */
public final class HttpHeaderNames {

    // Forked from Guava 27.1 at 8e174e76971449665658a800af6dd350806cc934
    // Changes:
    // - Added pseudo headers
    // - Added Accept-Patch
    // - Added Content-Base
    // - Added Prefer
    // - Removed the ancient CSP headers
    //   - X-Content-Security-Policy
    //   - X-Content-Security-Policy-Report-Only
    //   - X-WebKit-CSP
    //   - X-WebKit-CSP-Report-Only
    // - Removed Sec-Metadata headers (too early to add)
    //   - Sec-Fetch-Dest
    //   - Sec-Fetch-Mode
    //   - Sec-Fetch-Site
    //   - Sec-Fetch-User
    //   - Sec-Metadata

    // Pseudo-headers

    /**
     * The HTTP {@code ":method"} pseudo header field name.
     */
    public static final AsciiString METHOD = create(":method");
    /**
     * The HTTP {@code ":scheme"} pseudo header field name.
     */
    public static final AsciiString SCHEME = create(":scheme");
    /**
     * The HTTP {@code ":authority"} pseudo header field name.
     */
    public static final AsciiString AUTHORITY = create(":authority");
    /**
     * The HTTP {@code ":path"} pseudo header field name.
     */
    public static final AsciiString PATH = create(":path");
    /**
     * The HTTP {@code ":status"} pseudo header field name.
     */
    public static final AsciiString STATUS = create(":status");

    // HTTP Request and Response header fields

    /**
     * The HTTP {@code "Cache-Control"} header field name.
     */
    public static final AsciiString CACHE_CONTROL = create("Cache-Control");
    /**
     * The HTTP {@code "Content-Length"} header field name.
     */
    public static final AsciiString CONTENT_LENGTH = create("Content-Length");
    /**
     * The HTTP {@code "Content-Type"} header field name.
     */
    public static final AsciiString CONTENT_TYPE = create("Content-Type");
    /**
     * The HTTP {@code "Date"} header field name.
     */
    public static final AsciiString DATE = create("Date");
    /**
     * The HTTP {@code "Pragma"} header field name.
     */
    public static final AsciiString PRAGMA = create("Pragma");
    /**
     * The HTTP {@code "Via"} header field name.
     */
    public static final AsciiString VIA = create("Via");
    /**
     * The HTTP {@code "Warning"} header field name.
     */
    public static final AsciiString WARNING = create("Warning");

    // HTTP Request header fields

    /**
     * The HTTP {@code "Accept"} header field name.
     */
    public static final AsciiString ACCEPT = create("Accept");
    /**
     * The HTTP {@code "Accept-Charset"} header field name.
     */
    public static final AsciiString ACCEPT_CHARSET = create("Accept-Charset");
    /**
     * The HTTP {@code "Accept-Encoding"} header field name.
     */
    public static final AsciiString ACCEPT_ENCODING = create("Accept-Encoding");
    /**
     * The HTTP {@code "Accept-Language"} header field name.
     */
    public static final AsciiString ACCEPT_LANGUAGE = create("Accept-Language");
    /**
     * The HTTP {@code "Access-Control-Request-Headers"} header field name.
     */
    public static final AsciiString ACCESS_CONTROL_REQUEST_HEADERS = create("Access-Control-Request-Headers");
    /**
     * The HTTP {@code "Access-Control-Request-Method"} header field name.
     */
    public static final AsciiString ACCESS_CONTROL_REQUEST_METHOD = create("Access-Control-Request-Method");
    /**
     * The HTTP {@code "Authorization"} header field name.
     */
    public static final AsciiString AUTHORIZATION = create("Authorization");
    /**
     * The HTTP {@code "Connection"} header field name.
     */
    public static final AsciiString CONNECTION = create("Connection");
    /**
     * The HTTP {@code "Cookie"} header field name.
     */
    public static final AsciiString COOKIE = create("Cookie");
    /**
     * The HTTP {@code "Early-Data"} header field
     * name.
     */
    public static final AsciiString EARLY_DATA = create("Early-Data");
    /**
     * The HTTP {@code "Expect"} header field name.
     */
    public static final AsciiString EXPECT = create("Expect");
    /**
     * The HTTP {@code "From"} header field name.
     */
    public static final AsciiString FROM = create("From");
    /**
     * The HTTP {@code "Forwarded"} header field name.
     */
    public static final AsciiString FORWARDED = create("Forwarded");
    /**
     * The HTTP {@code "Follow-Only-When-Prerender-Shown"} header field name.
     */
    public static final AsciiString FOLLOW_ONLY_WHEN_PRERENDER_SHOWN =
            create("Follow-Only-When-Prerender-Shown");
    /**
     * The HTTP {@code "Host"} header field name.
     */
    public static final AsciiString HOST = create("Host");
    /**
     * The HTTP {@code "HTTP2-Settings"}
     *  header field name.
     */
    public static final AsciiString HTTP2_SETTINGS = create("HTTP2-Settings");
    /**
     * The HTTP {@code "If-Match"} header field name.
     */
    public static final AsciiString IF_MATCH = create("If-Match");
    /**
     * The HTTP {@code "If-Modified-Since"} header field name.
     */
    public static final AsciiString IF_MODIFIED_SINCE = create("If-Modified-Since");
    /**
     * The HTTP {@code "If-None-Match"} header field name.
     */
    public static final AsciiString IF_NONE_MATCH = create("If-None-Match");
    /**
     * The HTTP {@code "If-Range"} header field name.
     */
    public static final AsciiString IF_RANGE = create("If-Range");
    /**
     * The HTTP {@code "If-Unmodified-Since"} header field name.
     */
    public static final AsciiString IF_UNMODIFIED_SINCE = create("If-Unmodified-Since");
    /**
     * The HTTP {@code "Last-Event-ID"} header field name.
     */
    public static final AsciiString LAST_EVENT_ID = create("Last-Event-ID");
    /**
     * The HTTP {@code "Max-Forwards"} header field name.
     */
    public static final AsciiString MAX_FORWARDS = create("Max-Forwards");
    /**
     * The HTTP {@code "Origin"} header field name.
     */
    public static final AsciiString ORIGIN = create("Origin");
    /**
     * The HTTP {@code "Prefer"} header field name.
     */
    public static final AsciiString PREFER = create("Prefer");
    /**
     * The HTTP {@code "Proxy-Authorization"} header field name.
     */
    public static final AsciiString PROXY_AUTHORIZATION = create("Proxy-Authorization");
    /**
     * The HTTP {@code "Range"} header field name.
     */
    public static final AsciiString RANGE = create("Range");
    /**
     * The HTTP {@code "Referer"} header field name.
     */
    public static final AsciiString REFERER = create("Referer");
    /**
     * The HTTP {@code "Referrer-Policy"} header
     * field name.
     */
    public static final AsciiString REFERRER_POLICY = create("Referrer-Policy");

    /**
     * The HTTP {@code
     * Service-Worker} header field name.
     */
    public static final AsciiString SERVICE_WORKER = create("Service-Worker");
    /**
     * The HTTP {@code "TE"} header field name.
     */
    public static final AsciiString TE = create("TE");
    /**
     * The HTTP {@code "Upgrade"} header field name.
     */
    public static final AsciiString UPGRADE = create("Upgrade");
    /**
     * The HTTP {@code "User-Agent"} header field name.
     */
    public static final AsciiString USER_AGENT = create("User-Agent");

    // HTTP Response header fields

    /**
     * The HTTP {@code "Accept-Ranges"} header field name.
     */
    public static final AsciiString ACCEPT_RANGES = create("Accept-Ranges");
    /**
     * The HTTP {@code "Accept-Patch"} header field name.
     */
    public static final AsciiString ACCEPT_PATCH = create("Accept-Patch");
    /**
     * The HTTP {@code "Access-Control-Allow-Headers"} header field name.
     */
    public static final AsciiString ACCESS_CONTROL_ALLOW_HEADERS = create("Access-Control-Allow-Headers");
    /**
     * The HTTP {@code "Access-Control-Allow-Methods"} header field name.
     */
    public static final AsciiString ACCESS_CONTROL_ALLOW_METHODS = create("Access-Control-Allow-Methods");
    /**
     * The HTTP {@code "Access-Control-Allow-Origin"} header field name.
     */
    public static final AsciiString ACCESS_CONTROL_ALLOW_ORIGIN = create("Access-Control-Allow-Origin");
    /**
     * The HTTP {@code "Access-Control-Allow-Credentials"} header field name.
     */
    public static final AsciiString ACCESS_CONTROL_ALLOW_CREDENTIALS =
            create("Access-Control-Allow-Credentials");
    /**
     * The HTTP {@code "Access-Control-Expose-Headers"} header field name.
     */
    public static final AsciiString ACCESS_CONTROL_EXPOSE_HEADERS = create("Access-Control-Expose-Headers");
    /**
     * The HTTP {@code "Access-Control-Max-Age"} header field name.
     */
    public static final AsciiString ACCESS_CONTROL_MAX_AGE = create("Access-Control-Max-Age");
    /**
     * The HTTP {@code "Age"} header field name.
     */
    public static final AsciiString AGE = create("Age");
    /**
     * The HTTP {@code "Allow"} header field name.
     */
    public static final AsciiString ALLOW = create("Allow");
    /**
     * The HTTP {@code "Content-Base"} header field name.
     */
    public static final AsciiString CONTENT_BASE = create("Content-Base");
    /**
     * The HTTP {@code "Content-Disposition"} header field name.
     */
    public static final AsciiString CONTENT_DISPOSITION = create("Content-Disposition");
    /**
     * The HTTP {@code "Content-Encoding"} header field name.
     */
    public static final AsciiString CONTENT_ENCODING = create("Content-Encoding");
    /**
     * The HTTP {@code "Content-Language"} header field name.
     */
    public static final AsciiString CONTENT_LANGUAGE = create("Content-Language");
    /**
     * The HTTP {@code "Content-Location"} header field name.
     */
    public static final AsciiString CONTENT_LOCATION = create("Content-Location");
    /**
     * The HTTP {@code "Content-MD5"} header field name.
     */
    public static final AsciiString CONTENT_MD5 = create("Content-MD5");
    /**
     * The HTTP {@code "Content-Range"} header field name.
     */
    public static final AsciiString CONTENT_RANGE = create("Content-Range");
    /**
     * The HTTP {@code
     * Content-Security-Policy} header field name.
     */
    public static final AsciiString CONTENT_SECURITY_POLICY = create("Content-Security-Policy");
    /**
     * The HTTP
     * {@code "Content-Security-Policy-Report-Only"} header field name.
     */
    public static final AsciiString CONTENT_SECURITY_POLICY_REPORT_ONLY =
            create("Content-Security-Policy-Report-Only");
    /**
     * The HTTP {@code "ETag"} header field name.
     */
    public static final AsciiString ETAG = create("ETag");
    /**
     * The HTTP {@code "Expires"} header field name.
     */
    public static final AsciiString EXPIRES = create("Expires");
    /**
     * The HTTP {@code "Last-Modified"} header field name.
     */
    public static final AsciiString LAST_MODIFIED = create("Last-Modified");
    /**
     * The HTTP {@code "Link"} header field name.
     */
    public static final AsciiString LINK = create("Link");
    /**
     * The HTTP {@code "Location"} header field name.
     */
    public static final AsciiString LOCATION = create("Location");
    /**
     * The HTTP {@code "Origin-Trial"}
     * header field name.
     */
    public static final AsciiString ORIGIN_TRIAL = create("Origin-Trial");
    /**
     * The HTTP {@code "P3P"} header field name. Limited browser support.
     */
    public static final AsciiString P3P = create("P3P");
    /**
     * The HTTP {@code "Proxy-Authenticate"} header field name.
     */
    public static final AsciiString PROXY_AUTHENTICATE = create("Proxy-Authenticate");
    /**
     * The HTTP {@code "Refresh"} header field name. Non-standard header supported by most browsers.
     */
    public static final AsciiString REFRESH = create("Refresh");
    /**
     * The HTTP {@code "Report-To"} header field name.
     */
    public static final AsciiString REPORT_TO = create("Report-To");
    /**
     * The HTTP {@code "Retry-After"} header field name.
     */
    public static final AsciiString RETRY_AFTER = create("Retry-After");
    /**
     * The HTTP {@code "Server"} header field name.
     */
    public static final AsciiString SERVER = create("Server");
    /**
     * The HTTP {@code "Server-Timing"} header field
     * name.
     */
    public static final AsciiString SERVER_TIMING = create("Server-Timing");
    /**
     * The HTTP {@code
     * Service-Worker-Allowed} header field name.
     */
    public static final AsciiString SERVICE_WORKER_ALLOWED = create("Service-Worker-Allowed");
    /**
     * The HTTP {@code "Set-Cookie"} header field name.
     */
    public static final AsciiString SET_COOKIE = create("Set-Cookie");
    /**
     * The HTTP {@code "Set-Cookie2"} header field name.
     */
    public static final AsciiString SET_COOKIE2 = create("Set-Cookie2");

    /**
     * The HTTP {@code "SourceMap"} header field name.
     */
    public static final AsciiString SOURCE_MAP = create("SourceMap");

    /**
     * The HTTP {@code
     * Strict-Transport-Security} header field name.
     */
    public static final AsciiString STRICT_TRANSPORT_SECURITY = create("Strict-Transport-Security");
    /**
     * The HTTP {@code
     * Timing-Allow-Origin} header field name.
     */
    public static final AsciiString TIMING_ALLOW_ORIGIN = create("Timing-Allow-Origin");
    /**
     * The HTTP {@code "Trailer"} header field name.
     */
    public static final AsciiString TRAILER = create("Trailer");
    /**
     * The HTTP {@code "Transfer-Encoding"} header field name.
     */
    public static final AsciiString TRANSFER_ENCODING = create("Transfer-Encoding");
    /**
     * The HTTP {@code "Vary"} header field name.
     */
    public static final AsciiString VARY = create("Vary");
    /**
     * The HTTP {@code "WWW-Authenticate"} header field name.
     */
    public static final AsciiString WWW_AUTHENTICATE = create("WWW-Authenticate");

    // Common, non-standard HTTP header fields

    /**
     * The HTTP {@code "DNT"} header field name.
     */
    public static final AsciiString DNT = create("DNT");
    /**
     * The HTTP {@code "X-Content-Type-Options"} header field name.
     */
    public static final AsciiString X_CONTENT_TYPE_OPTIONS = create("X-Content-Type-Options");
    /**
     * The HTTP {@code "X-Do-Not-Track"} header field name.
     */
    public static final AsciiString X_DO_NOT_TRACK = create("X-Do-Not-Track");
    /**
     * The HTTP {@code "X-Forwarded-For"} header field name (superseded by {@code "Forwarded"}).
     */
    public static final AsciiString X_FORWARDED_FOR = create("X-Forwarded-For");
    /**
     * The HTTP {@code "X-Forwarded-Proto"} header field name.
     */
    public static final AsciiString X_FORWARDED_PROTO = create("X-Forwarded-Proto");
    /**
     * The HTTP {@code "X-Forwarded-Host"} header field name.
     */
    public static final AsciiString X_FORWARDED_HOST = create("X-Forwarded-Host");
    /**
     * The HTTP {@code "X-Forwarded-Port"} header field name.
     */
    public static final AsciiString X_FORWARDED_PORT = create("X-Forwarded-Port");
    /**
     * The HTTP {@code "X-Frame-Options"} header field name.
     */
    public static final AsciiString X_FRAME_OPTIONS = create("X-Frame-Options");
    /**
     * The HTTP {@code "X-Powered-By"} header field name.
     */
    public static final AsciiString X_POWERED_BY = create("X-Powered-By");
    /**
     * The HTTP {@code
     * Public-Key-Pins} header field name.
     */
    public static final AsciiString PUBLIC_KEY_PINS = create("Public-Key-Pins");
    /**
     * The HTTP {@code
     * Public-Key-Pins-Report-Only} header field name.
     */
    public static final AsciiString PUBLIC_KEY_PINS_REPORT_ONLY = create("Public-Key-Pins-Report-Only");
    /**
     * The HTTP {@code "X-Requested-With"} header field name.
     */
    public static final AsciiString X_REQUESTED_WITH = create("X-Requested-With");
    /**
     * The HTTP {@code "X-User-IP"} header field name.
     */
    public static final AsciiString X_USER_IP = create("X-User-IP");
    /**
     * The HTTP {@code "X-Download-Options"} header field name.
     *
     *
When the new X-Download-Options header is present with the value {@code "noopen"}, the user is
     * prevented from opening a file download directly; instead, they must first save the file
     * locally.
     */
    public static final AsciiString X_DOWNLOAD_OPTIONS = create("X-Download-Options");
    /**
     * The HTTP {@code "X-XSS-Protection"} header field name.
     */
    public static final AsciiString X_XSS_PROTECTION = create("X-XSS-Protection");
    /**
     * The HTTP {@code
     * X-DNS-Prefetch-Control} header controls DNS prefetch behavior. Value can be "on" or "off".
     * By default, DNS prefetching is "on" for HTTP pages and "off" for HTTPS pages.
     */
    public static final AsciiString X_DNS_PREFETCH_CONTROL = create("X-DNS-Prefetch-Control");
    /**
     * The HTTP
     * {@code "Ping-From"} header field name.
     */
    public static final AsciiString PING_FROM = create("Ping-From");
    /**
     * The HTTP
     * {@code "Ping-To"} header field name.
     */
    public static final AsciiString PING_TO = create("Ping-To");
    /**
     * The HTTP {@code
     * Sec-Token-Binding} header field name.
     */
    public static final AsciiString SEC_TOKEN_BINDING = create("Sec-Token-Binding");
    /**
     * The HTTP {@code
     * Sec-Provided-Token-Binding-ID} header field name.
     */
    public static final AsciiString SEC_PROVIDED_TOKEN_BINDING_ID = create("Sec-Provided-Token-Binding-ID");
    /**
     * The HTTP {@code
     * Sec-Referred-Token-Binding-ID} header field name.
     */
    public static final AsciiString SEC_REFERRED_TOKEN_BINDING_ID = create("Sec-Referred-Token-Binding-ID");

    private static final Map<CharSequence, AsciiString> map;

    static {
        final ImmutableMap.Builder<CharSequence, AsciiString> builder = ImmutableMap.builder();
        for (Field f : HttpHeaderNames.class.getDeclaredFields()) {
            final int m = f.getModifiers();
            if (Modifier.isPublic(m) && Modifier.isStatic(m) && Modifier.isFinal(m) &&
                f.getType() == AsciiString.class) {
                final AsciiString name;
                try {
                    name = (AsciiString) f.get(null);
                } catch (Exception e) {
                    throw new Error(e);
                }
                builder.put(name, name);
                builder.put(name.toString(), name);
            }
        }
        map = builder.build();
    }

    /**
     * Lower-cases and converts the specified header name into an {@link AsciiString}. If {@code "name"} is
     * a known header name, this method will return a pre-instantiated {@link AsciiString} to reduce
     * the allocation rate of {@link AsciiString}.
     */
    public static AsciiString of(CharSequence name) {
        if (name instanceof AsciiString) {
            return of((AsciiString) name);
        }

        final String lowerCased = Ascii.toLowerCase(requireNonNull(name, "name"));
        final AsciiString cached = map.get(lowerCased);
        return cached != null ? cached : AsciiString.cached(lowerCased);
    }

    /**
     * Lower-cases and converts the specified header name into an {@link AsciiString}. If {@code "name"} is
     * a known header name, this method will return a pre-instantiated {@link AsciiString} to reduce
     * the allocation rate of {@link AsciiString}.
     */
    public static AsciiString of(AsciiString name) {
        final AsciiString lowerCased = name.toLowerCase();
        final AsciiString cached = map.get(lowerCased);
        return cached != null ? cached : lowerCased;
    }

    private static AsciiString create(String name) {
        return AsciiString.cached(Ascii.toLowerCase(name));
    }

    private HttpHeaderNames() {}
}

## 内容2
### 文件路径：
core/src/main/java/com/linecorp/armeria/common/HttpHeadersBase.java
### 代码内容：
package com.linecorp.armeria.common;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.linecorp.armeria.internal.ArmeriaHttpUtil.isAbsoluteUri;
import static io.netty.handler.codec.http2.Http2Headers.PseudoHeaderName.hasPseudoHeaderFormat;
import static io.netty.util.internal.MathUtil.findNextPositivePowerOfTwo;
import static java.lang.Math.max;
import static java.lang.Math.min;
import static java.util.Objects.requireNonNull;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

import javax.annotation.Nullable;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterators;

import io.netty.handler.codec.DateFormatter;
import io.netty.util.AsciiString;

/**
 * The base container implementation of HTTP/2 headers.
 */
class HttpHeadersBase implements HttpHeaderGetters {

    static final int DEFAULT_SIZE_HINT = 16;

    /**
     * Constant used to seed the hash code generation. Could be anything but this was borrowed from murmur3.
     */
    static final int HASH_CODE_SEED = 0xc2b2ae35;

    // XXX(anuraaga): It could be an interesting future optimization if we can use something similar
    //                to an EnumSet when it's applicable, with just one each of commonly known header names.
    //                It should be very common.
    @VisibleForTesting
    final HeaderEntry[] entries;
    private final byte hashMask;

    private final HeaderEntry head;
    private HeaderEntry firstNonPseudo;

    int size;
    private boolean endOfStream;

    HttpHeadersBase(int sizeHint) {
        // Enforce a bound of [2, 128] because hashMask is a byte. The max possible value of hashMask is
        // one less than the length of this array, and we want the mask to be > 0.
        entries = new HeaderEntry[findNextPositivePowerOfTwo(max(2, min(sizeHint, 128)))];
        hashMask = (byte) (entries.length - 1);
        head = firstNonPseudo = new HeaderEntry();
    }

    /**
     * Creates a shallow or deep copy of the specified {@link HttpHeadersBase}.
     */
    HttpHeadersBase(HttpHeadersBase headers, boolean shallowCopy) {
        hashMask = headers.hashMask;
        endOfStream = headers.endOfStream;

        if (shallowCopy) {
            entries = headers.entries;
            head = headers.head;
            firstNonPseudo = headers.firstNonPseudo;
            size = headers.size;
        } else {
            entries = new HeaderEntry[headers.entries.length];
            head = firstNonPseudo = new HeaderEntry();
            final boolean succeeded = addFast(headers);
            assert succeeded;
        }
    }

    /**
     * Creates a deep copy of the specified {@link HttpHeaderGetters}.
     */
    HttpHeadersBase(HttpHeaderGetters headers) {
        this(headers.size());
        assert !(headers instanceof HttpHeadersBase);
        addSlow(headers);
    }

    // Shortcut methods

    URI uri() {
        final String uri;
        final String path = path();
        if (isAbsoluteUri(path)) {
            uri = path;
        } else {
            final String scheme = scheme();
            checkState(scheme != null, ":scheme header does not exist.");
            final String authority = authority();
            checkState(authority != null, ":authority header does not exist.");
            uri = scheme + "://" + authority + path;
        }

        try {
            return new URI(uri);
        } catch (URISyntaxException e) {
            throw new IllegalStateException("not a valid URI: " + uri, e);
        }
    }

    HttpMethod method() {
        final String methodStr = get(HttpHeaderNames.METHOD);
        checkState(methodStr != null, ":method header does not exist.");
        return HttpMethod.isSupported(methodStr) ? HttpMethod.valueOf(methodStr)
                                                 : HttpMethod.UNKNOWN;
    }

    final void method(HttpMethod method) {
        requireNonNull(method, "method");
        set(HttpHeaderNames.METHOD, method.name());
    }

    @Nullable
    String scheme() {
        return get(HttpHeaderNames.SCHEME);
    }

    final void scheme(String scheme) {
        requireNonNull(scheme, "scheme");
        set(HttpHeaderNames.SCHEME, scheme);
    }

    @Nullable
    String authority() {
        return get(HttpHeaderNames.AUTHORITY);
    }

    final void authority(String authority) {
        requireNonNull(authority, "authority");
        set(HttpHeaderNames.AUTHORITY, authority);
    }

    String path() {
        final String path = get(HttpHeaderNames.PATH);
        checkState(path != null, ":path header does not exist.");
        return path;
    }

    final void path(String path) {
        requireNonNull(path, "path");
        set(HttpHeaderNames.PATH, path);
    }

    HttpStatus status() {
        final String statusStr = get(HttpHeaderNames.STATUS);
        checkState(statusStr != null, ":status header does not exist.");
        return HttpStatus.valueOf(statusStr);
    }

    final void status(int statusCode) {
        status(HttpStatus.valueOf(statusCode));
    }

    final void status(HttpStatus status) {
        requireNonNull(status, "status");
        set(HttpHeaderNames.STATUS, status.codeAsText());
    }

    @Nullable
    @Override
    public MediaType contentType() {
        final String contentTypeString = get(HttpHeaderNames.CONTENT_TYPE);
        if (contentTypeString == null) {
            return null;
        }

        try {
            return MediaType.parse(contentTypeString);
        } catch (IllegalArgumentException unused) {
            // Invalid media type
            return null;
        }
    }

    final void contentType(MediaType contentType) {
        requireNonNull(contentType, "contentType");
        set(HttpHeaderNames.CONTENT_TYPE, contentType.toString());
    }

    // Getters

    @Override
    public final boolean isEndOfStream() {
        return endOfStream;
    }

    @Nullable
    @Override
    public final String get(CharSequence name) {
        requireNonNull(name, "name");
        final int h = AsciiString.hashCode(name);
        final int i = index(h);
        HeaderEntry e = entries[i];
        String value = null;
        // loop until the first header was found
        while (e != null) {
            if (e.hash == h && keyEquals(e.key, name)) {
                value = e.value;
            }
            e = e.next;
        }
        return value;
    }

    @Override
    public final String get(CharSequence name, String defaultValue) {
        requireNonNull(defaultValue, "defaultValue");
        final String value = get(name);
        return value != null ? value : defaultValue;
    }

    @Override
    public final List<String> getAll(CharSequence name) {
        requireNonNull(name, "name");
        return getAllReversed(name).reverse();
    }

    private ImmutableList<String> getAllReversed(CharSequence name) {
        final int h = AsciiString.hashCode(name);
        final int i = index(h);
        HeaderEntry e = entries[i];

        if (e == null) {
            return ImmutableList.of();
        }

        final ImmutableList.Builder<String> builder = ImmutableList.builder();
        do {
            if (e.hash == h && keyEquals(e.key, name)) {
                builder.add(e.getValue());
            }
            e = e.next;
        } while (e != null);
        return builder.build();
    }

    @Nullable
    @Override
    public final Integer getInt(CharSequence name) {
        final String v = get(name);
        return toInteger(v);
    }

    @Override
    public final int getInt(CharSequence name, int defaultValue) {
        final Integer v = getInt(name);
        return v != null ? v : defaultValue;
    }

    @Nullable
    @Override
    public final Long getLong(CharSequence name) {
        final String v = get(name);
        return toLong(v);
    }

    @Override
    public final long getLong(CharSequence name, long defaultValue) {
        final Long v = getLong(name);
        return v != null ? v : defaultValue;
    }

    @Nullable
    @Override
    public final Float getFloat(CharSequence name) {
        final String v = get(name);
        return toFloat(v);
    }

    @Override
    public final float getFloat(CharSequence name, float defaultValue) {
        final Float v = getFloat(name);
        return v != null ? v : defaultValue;
    }

    @Nullable
    @Override
    public final Double getDouble(CharSequence name) {
        final String v = get(name);
        return toDouble(v);
    }

    @Override
    public final double getDouble(CharSequence name, double defaultValue) {
        final Double v = getDouble(name);
        return v != null ? v : defaultValue;
    }

    @Nullable
    @Override
    public final Long getTimeMillis(CharSequence name) {
        final String v = get(name);
        return toTimeMillis(v);
    }

    @Override
    public final long getTimeMillis(CharSequence name, long defaultValue) {
        final Long v = getTimeMillis(name);
        return v != null ? v : defaultValue;
    }

    @Override
    public final boolean contains(CharSequence name) {
        requireNonNull(name, "name");
        final int h = AsciiString.hashCode(name);
        final int i = index(h);
        HeaderEntry e = entries[i];
        // loop until the first header was found
        while (e != null) {
            if (e.hash == h && keyEquals(e.key, name)) {
                return true;
            }
            e = e.next;
        }
        return false;
    }

    @Override
    public final boolean contains(CharSequence name, String value) {
        requireNonNull(name, "name");
        requireNonNull(value, "value");
        final int h = AsciiString.hashCode(name);
        final int i = index(h);
        HeaderEntry e = entries[i];
        while (e != null) {
            if (e.hash == h && keyEquals(e.key, name) &&
                AsciiString.contentEquals(e.value, value)) {
                return true;
            }
            e = e.next;
        }
        return false;
    }

    @Override
    public final boolean containsObject(CharSequence name, Object value) {
        requireNonNull(value, "value");
        return contains(name, fromObject(value));
    }

    @Override
    public final boolean containsInt(CharSequence name, int value) {
        return contains(name, String.valueOf(value));
    }

    @Override
    public final boolean containsLong(CharSequence name, long value) {
        return contains(name, String.valueOf(value));
    }

    @Override
    public final boolean containsFloat(CharSequence name, float value) {
        return contains(name, String.valueOf(value));
    }

    @Override
    public final boolean containsDouble(CharSequence name, double value) {
        return contains(name, String.valueOf(value));
    }

    @Override
    public final boolean containsTimeMillis(CharSequence name, long value) {
        return contains(name, fromTimeMillis(value));
    }

    @Override
    public final int size() {
        return size;
    }

    @Override
    public final boolean isEmpty() {
        return size() == 0;
    }

    @Override
    public final Set<AsciiString> names() {
        if (isEmpty()) {
            return ImmutableSet.of();
        }
        final ImmutableSet.Builder<AsciiString> builder = ImmutableSet.builder();
        HeaderEntry e = head.after;
        while (e != head) {
            builder.add(e.getKey());
            e = e.after;
        }
        return builder.build();
    }

    @Override
    public final Iterator<Entry<AsciiString, String>> iterator() {
        return new HeaderIterator();
    }

    @Override
    public final Iterator<String> valueIterator(CharSequence name) {
        return getAll(name).iterator();
    }

    @Override
    public final void forEach(BiConsumer<AsciiString, String> action) {
        requireNonNull(action, "action");
        for (Entry<AsciiString, String> e : this) {
            action.accept(e.getKey(), e.getValue());
        }
    }

    @Override
    public final void forEachValue(CharSequence name, Consumer<String> action) {
        requireNonNull(name, "name");
        requireNonNull(action, "action");
        for (final Iterator<String> i = valueIterator(name); i.hasNext();) {
            action.accept(i.next());
        }
    }

    // Mutators

    final void endOfStream(boolean endOfStream) {
        this.endOfStream = endOfStream;
    }

    @Nullable
    final String getAndRemove(CharSequence name) {
        requireNonNull(name, "name");
        final int h = AsciiString.hashCode(name);
        return remove0(h, index(h), name);
    }

    final String getAndRemove(CharSequence name, String defaultValue) {
        requireNonNull(defaultValue, "defaultValue");
        final String value = getAndRemove(name);
        return value != null ? value : defaultValue;
    }

    final List<String> getAllAndRemove(CharSequence name) {
        final List<String> all = getAll(name);
        if (!all.isEmpty()) {
            remove(name);
        }
        return all;
    }

    @Nullable
    final Integer getIntAndRemove(CharSequence name) {
        final String v = getAndRemove(name);
        return toInteger(v);
    }

    final int getIntAndRemove(CharSequence name, int defaultValue) {
        final Integer v = getIntAndRemove(name);
        return v != null ? v : defaultValue;
    }

    @Nullable
    final Long getLongAndRemove(CharSequence name) {
        final String v = getAndRemove(name);
        return toLong(v);
    }

    final long getLongAndRemove(CharSequence name, long defaultValue) {
        final Long v = getLongAndRemove(name);
        return v != null ? v : defaultValue;
    }

    @Nullable
    final Float getFloatAndRemove(CharSequence name) {
        final String v = getAndRemove(name);
        return toFloat(v);
    }

    final float getFloatAndRemove(CharSequence name, float defaultValue) {
        final Float v = getFloatAndRemove(name);
        return v != null ? v : defaultValue;
    }

    @Nullable
    final Double getDoubleAndRemove(CharSequence name) {
        final String v = getAndRemove(name);
        return toDouble(v);
    }

    final double getDoubleAndRemove(CharSequence name, double defaultValue) {
        final Double v = getDoubleAndRemove(name);
        return v != null ? v : defaultValue;
    }

    @Nullable
    final Long getTimeMillisAndRemove(CharSequence name) {
        final String v = getAndRemove(name);
        return toTimeMillis(v);
    }

    final long getTimeMillisAndRemove(CharSequence name, long defaultValue) {
        final Long v = getTimeMillisAndRemove(name);
        return v != null ? v : defaultValue;
    }

    final void add(CharSequence name, String value) {
        final AsciiString normalizedName = normalizeName(name);
        requireNonNull(value, "value");
        final int h = normalizedName.hashCode();
        final int i = index(h);
        add0(h, i, normalizedName, value);
    }

    final void add(CharSequence name, Iterable<String> values) {
        final AsciiString normalizedName = normalizeName(name);
        requireNonNull(values, "values");
        final int h = normalizedName.hashCode();
        final int i = index(h);
        for (String v : values) {
            requireNonNullElement(values, v);
            add0(h, i, normalizedName, v);
        }
    }

    final void add(CharSequence name, String... values) {
        final AsciiString normalizedName = normalizeName(name);
        requireNonNull(values, "values");
        final int h = normalizedName.hashCode();
        final int i = index(h);
        for (String v : values) {
            requireNonNullElement(values, v);
            add0(h, i, normalizedName, v);
        }
    }

    final void add(Iterable<? extends Entry<? extends CharSequence, String>> headers) {
        if (headers == this) {
            throw new IllegalArgumentException("can't add to itself.");
        }

        if (!addFast(headers)) {
            addSlow(headers);
        }
    }

    final void addObject(CharSequence name, Object value) {
        requireNonNull(value, "value");
        add(name, fromObject(value));
    }

    final void addObject(CharSequence name, Iterable<?> values) {
        final AsciiString normalizedName = normalizeName(name);
        requireNonNull(values, "values");
        for (Object v : values) {
            requireNonNullElement(values, v);
            addObject(normalizedName, v);
        }
    }

    final void addObject(CharSequence name, Object... values) {
        final AsciiString normalizedName = normalizeName(name);
        requireNonNull(values, "values");
        for (Object v : values) {
            requireNonNullElement(values, v);
            addObject(normalizedName, v);
        }
    }

    void addObject(Iterable<? extends Entry<? extends CharSequence, ?>> headers) {
        if (headers == this) {
            throw new IllegalArgumentException("can't add to itself.");
        }

        if (!addFast(headers)) {
            addObjectSlow(headers);
        }
    }

    final void addInt(CharSequence name, int value) {
        add(name, String.valueOf(value));
    }

    final void addLong(CharSequence name, long value) {
        add(name, String.valueOf(value));
    }

    final void addFloat(CharSequence name, float value) {
        add(name, String.valueOf(value));
    }

    final void addDouble(CharSequence name, double value) {
        add(name, String.valueOf(value));
    }

    final void addTimeMillis(CharSequence name, long value) {
        add(name, fromTimeMillis(value));
    }

    final void set(CharSequence name, String value) {
        final AsciiString normalizedName = normalizeName(name);
        requireNonNull(value, "value");
        final int h = normalizedName.hashCode();
        final int i = index(h);
        remove0(h, i, normalizedName);
        add0(h, i, normalizedName, value);
    }

    final void set(CharSequence name, Iterable<String> values) {
        final AsciiString normalizedName = normalizeName(name);
        requireNonNull(values, "values");

        final int h = normalizedName.hashCode();
        final int i = index(h);

        remove0(h, i, normalizedName);
        for (String v : values) {
            requireNonNullElement(values, v);
            add0(h, i, normalizedName, v);
        }
    }

    final void set(CharSequence name, String... values) {
        final AsciiString normalizedName = normalizeName(name);
        requireNonNull(values, "values");

        final int h = normalizedName.hashCode();
        final int i = index(h);

        remove0(h, i, normalizedName);
        for (String v : values) {
            requireNonNullElement(values, v);
            add0(h, i, normalizedName, v);
        }
    }

    final void set(Iterable<? extends Entry<? extends CharSequence, String>> headers) {
        requireNonNull(headers, "headers");
        if (headers == this) {
            return;
        }

        for (Entry<? extends CharSequence, String> e : headers) {
            remove(e.getKey());
        }

        if (!addFast(headers)) {
            addSlow(headers);
        }
    }

    public HttpHeadersBase setIfAbsent(Iterable<? extends Entry<? extends CharSequence, String>> headers) {
        requireNonNull(headers, "headers");
        final Set<AsciiString> existingNames = names();
        if (!setIfAbsentFast(headers, existingNames)) {
            setIfAbsentSlow(headers, existingNames);
        }
        return this;
    }

    private boolean setIfAbsentFast(Iterable<? extends Entry<? extends CharSequence, String>> headers,
                                    Set<AsciiString> existingNames) {

        if (!(headers instanceof HttpHeadersBase)) {
            return false;
        }

        final HttpHeadersBase headersBase = (HttpHeadersBase) headers;
        HeaderEntry e = headersBase.head.after;

        while (e != headersBase.head) {
            final AsciiString key = e.key;
            final String value = e.value;
            assert key != null;
            assert value != null;
            if (!existingNames.contains(key)) {
                add0(e.hash, index(e.hash), key, value);
            }
            e = e.after;
        }

        return true;
    }

    private void setIfAbsentSlow(Iterable<? extends Entry<? extends CharSequence, String>> headers,
                                 Set<AsciiString> existingNames) {

        for (Entry<? extends CharSequence, String> header : headers) {
            final AsciiString key = AsciiString.of(header.getKey());
            if (!existingNames.contains(key)) {
                add(key, header.getValue());
            }
        }
    }

    final void setObject(CharSequence name, Object value) {
        requireNonNull(value, "value");
        set(name, fromObject(value));
    }

    final void setObject(CharSequence name, Iterable<?> values) {
        final AsciiString normalizedName = normalizeName(name);
        requireNonNull(values, "values");

        final int h = normalizedName.hashCode();
        final int i = index(h);

        remove0(h, i, normalizedName);
        for (Object v: values) {
            requireNonNullElement(values, v);
            add0(h, i, normalizedName, fromObject(v));
        }
    }

    final void setObject(CharSequence name, Object... values) {
        final AsciiString normalizedName = normalizeName(name);
        requireNonNull(values, "values");

        final int h = normalizedName.hashCode();
        final int i = index(h);

        remove0(h, i, normalizedName);
        for (Object v: values) {
            requireNonNullElement(values, v);
            add0(h, i, normalizedName, fromObject(v));
        }
    }

    final void setObject(Iterable<? extends Entry<? extends CharSequence, ?>> headers) {
        requireNonNull(headers, "headers");
        if (headers == this) {
            return;
        }

        for (Entry<? extends CharSequence, ?> e : headers) {
            remove(e.getKey());
        }

        if (!addFast(headers)) {
            addObjectSlow(headers);
        }
    }

    final void setInt(CharSequence name, int value) {
        set(name, String.valueOf(value));
    }

    final void setLong(CharSequence name, long value) {
        set(name, String.valueOf(value));
    }

    final void setFloat(CharSequence name, float value) {
        set(name, String.valueOf(value));
    }

    final void setDouble(CharSequence name, double value) {
        set(name, String.valueOf(value));
    }

    final void setTimeMillis(CharSequence name, long value) {
        set(name, fromTimeMillis(value));
    }

    final boolean remove(CharSequence name) {
        requireNonNull(name, "name");
        final int h = AsciiString.hashCode(name);
        return remove0(h, index(h), name) != null;
    }

    final void clear() {
        Arrays.fill(entries, null);
        firstNonPseudo = head.before = head.after = head;
        size = 0;
    }

    private static AsciiString normalizeName(CharSequence name) {
        checkArgument(requireNonNull(name, "name").length() > 0, "name is empty.");
        return HttpHeaderNames.of(name);
    }

    private static void requireNonNullElement(Object values, @Nullable Object e) {
        if (e == null) {
            throw new NullPointerException("values contains null: " + values);
        }
    }

    private int index(int hash) {
        return hash & hashMask;
    }

    private void add0(int h, int i, AsciiString name, String value) {
        // Update the hash table.
        entries[i] = new HeaderEntry(h, name, value, entries[i]);
        ++size;
    }

    private boolean addFast(Iterable<? extends Entry<? extends CharSequence, ?>> headers) {
        if (!(headers instanceof HttpHeadersBase)) {
            return false;
        }

        final HttpHeadersBase headersBase = (HttpHeadersBase) headers;
        HeaderEntry e = headersBase.head.after;
        while (e != headersBase.head) {
            final AsciiString key = e.key;
            final String value = e.value;
            assert key != null;
            assert value != null;
            add0(e.hash, index(e.hash), key, value);
            e = e.after;
        }

        return true;
    }

    private void addSlow(Iterable<? extends Entry<? extends CharSequence, String>> headers) {
        // Slow copy
        for (Entry<? extends CharSequence, String> header : headers) {
            add(header.getKey(), header.getValue());
        }
    }

    private void addObjectSlow(Iterable<? extends Entry<? extends CharSequence, ?>> headers) {
        // Slow copy
        for (Entry<? extends CharSequence, ?> header : headers) {
            addObject(header.getKey(), header.getValue());
        }
    }

    /**
     * Removes all the entries whose hash code equals {@code h} and whose name is equal to {@code name}.
     *
     * @return the first value inserted, or {@code null} if there is no such header.
     */
    @Nullable
    private String remove0(int h, int i, CharSequence name) {
        HeaderEntry e = entries[i];
        if (e == null) {
            return null;
        }

        String value = null;
        HeaderEntry next = e.next;
        while (next != null) {
            if (next.hash == h && keyEquals(next.key, name)) {
                value = next.value;
                e.next = next.next;
                next.remove();
                --size;
            } else {
                e = next;
            }

            next = e.next;
        }

        e = entries[i];
        if (e.hash == h && keyEquals(e.key, name)) {
            if (value == null) {
                value = e.value;
            }
            entries[i] = e.next;
            e.remove();
            --size;
        }

        return value;
    }

    private static boolean keyEquals(@Nullable AsciiString a, CharSequence b) {
        return a != null && (a == b || a.contentEqualsIgnoreCase(b));
    }

    // Conversion functions

    @Nullable
    private static Integer toInteger(@Nullable String v) {
        try {
            return v != null ? Integer.parseInt(v) : null;
        } catch (NumberFormatException ignore) {
            return null;
        }
    }

    @Nullable
    private static Long toLong(@Nullable String v) {
        try {
            return v != null ? Long.parseLong(v) : null;
        } catch (NumberFormatException ignore) {
            return null;
        }
    }

    @Nullable
    private static Float toFloat(@Nullable String v) {
        try {
            return v != null ? Float.parseFloat(v) : null;
        } catch (NumberFormatException ignore) {
            return null;
        }
    }

    @Nullable
    private static Double toDouble(@Nullable String v) {
        try {
            return v != null ? Double.parseDouble(v) : null;
        } catch (NumberFormatException ignore) {
            return null;
        }
    }

    @Nullable
    private static Long toTimeMillis(@Nullable String v) {
        if (v == null) {
            return null;
        }

        try {
            @SuppressWarnings("UseOfObsoleteDateTimeApi")
            final Date date = DateFormatter.parseHttpDate(v);
            return date != null ? date.getTime() : null;
        } catch (Exception ignore) {
            // `parseHttpDate()` can raise an exception rather than returning `null`
            // when the given value has more than 64 characters.
            return null;
        }
    }

    private static String fromTimeMillis(long value) {
        return StringValueConverter.INSTANCE.convertTimeMillis(value);
    }

    private static String fromObject(Object value) {
        final String strVal = StringValueConverter.INSTANCE.convertObject(value);
        assert strVal != null : value + " converted to null.";
        return strVal;
    }

    // hashCode(), equals() and toString()

    @Override
    public final int hashCode() {
        int result = HASH_CODE_SEED;
        for (AsciiString name : names()) {
            result = (result * 31 + name.hashCode()) * 31 + getAll(name).hashCode();
        }
        return result;
    }

    @Override
    public boolean equals(@Nullable Object o) {
        if (this == o) {
            return true;
        }

        if (!(o instanceof HttpHeaderGetters)) {
            return false;
        }

        final HttpHeaderGetters that = (HttpHeaderGetters) o;
        if (isEndOfStream() != that.isEndOfStream() ||
            size() != that.size()) {
            return false;
        }

        if (that instanceof HttpHeadersBase) {
            return equalsFast((HttpHeadersBase) that);
        } else {
            return equalsSlow(that);
        }
    }

    private boolean equalsFast(HttpHeadersBase that) {
        HeaderEntry e = head.after;
        while (e != head) {
            final AsciiString name = e.getKey();
            if (!getAllReversed(name).equals(that.getAllReversed(name))) {
                return false;
            }
            e = e.after;
        }
        return true;
    }

    private boolean equalsSlow(HttpHeaderGetters that) {
        HeaderEntry e = head.after;
        while (e != head) {
            final AsciiString name = e.getKey();
            if (!Iterators.elementsEqual(valueIterator(name), that.valueIterator(name))) {
                return false;
            }
            e = e.after;
        }
        return true;
    }

    @Override
    public String toString() {
        if (size == 0) {
            return endOfStream ? "[EOS]" : "[]";
        }

        final StringBuilder sb = new StringBuilder(7 + size * 20);
        if (endOfStream) {
            sb.append("[EOS, ");
        } else {
            sb.append('[');
        }

        HeaderEntry e = head.after;
        while (e != head) {
            sb.append(e.key).append('=').append(e.value).append(", ");
            e = e.after;
        }

        final int length = sb.length();
        sb.setCharAt(length - 2, ']');
        return sb.substring(0, length - 1);
    }

    // Iterator implementations

    private final class HeaderIterator implements Iterator<Map.Entry<AsciiString, String>> {
        private HeaderEntry current = head;

        @Override
        public boolean hasNext() {
            return current.after != head;
        }

        @Override
        public Entry<AsciiString, String> next() {
            current = current.after;

            if (current == head) {
                throw new NoSuchElementException();
            }

            return current;
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException("read-only");
        }
    }

    private final class HeaderEntry implements Map.Entry<AsciiString, String> {

        final int hash;
        @Nullable
        final AsciiString key;
        @Nullable
        final String value;
        /**
         * In bucket linked list.
         */
        @Nullable
        HeaderEntry next;
        /**
         * Overall insertion order linked list.
         */
        HeaderEntry before;
        HeaderEntry after;

        /**
         * Creates a new head node.
         */
        HeaderEntry() {
            hash = -1;
            key = null;
            value = null;
            before = after = this;
        }

        HeaderEntry(int hash, AsciiString key, String value, HeaderEntry next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;

            // Make sure the pseudo headers fields are first in iteration order
            if (hasPseudoHeaderFormat(key)) {
                after = firstNonPseudo;
                before = firstNonPseudo.before;
            } else {
                after = head;
                before = head.before;
                if (firstNonPseudo == head) {
                    firstNonPseudo = this;
                }
            }
            pointNeighborsToThis();
        }

        void pointNeighborsToThis() {
            before.after = this;
            after.before = this;
        }

        void remove() {
            if (this == firstNonPseudo) {
                firstNonPseudo = firstNonPseudo.after;
            }

            before.after = after;
            after.before = before;
        }

        @Override
        public AsciiString getKey() {
            assert key != null;
            return key;
        }

        @Override
        public String getValue() {
            assert value != null;
            return value;
        }

        @Override
        public String setValue(String value) {
            throw new UnsupportedOperationException("read-only");
        }

        @Override
        public int hashCode() {
            return (key == null ? 0 : key.hashCode()) ^ AsciiString.hashCode(value);
        }

        @Override
        public boolean equals(@Nullable Object o) {
            if (this == o) {
                return true;
            }

            if (!(o instanceof Map.Entry)) {
                return false;
            }

            final Map.Entry<?, ?> that = (Map.Entry<?, ?>) o;
            final Object thatKey = that.getKey();
            return thatKey instanceof AsciiString &&
                   keyEquals(key, (CharSequence) thatKey) &&
                   Objects.equals(value, that.getValue());
        }

        @Override
        public String toString() {
            if (key == null) {
                return "";
            }

            assert value != null;
            return new StringBuilder(key.length() + value.length() + 1)
                    .append(key)
                    .append('=')
                    .append(value)
                    .toString();
        }
    }
}

## 内容3
### 文件路径：
core/src/main/java/com/linecorp/armeria/internal/ArmeriaHttpUtil.java
### 代码内容：
package com.linecorp.armeria.internal;

import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static io.netty.handler.codec.http.HttpUtil.isAsteriskForm;
import static io.netty.handler.codec.http.HttpUtil.isOriginForm;
import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
import static io.netty.handler.codec.http2.Http2Exception.streamError;
import static io.netty.util.AsciiString.EMPTY_STRING;
import static io.netty.util.ByteProcessor.FIND_COMMA;
import static io.netty.util.internal.StringUtil.decodeHexNibble;
import static io.netty.util.internal.StringUtil.isNullOrEmpty;
import static io.netty.util.internal.StringUtil.length;
import static java.util.Objects.requireNonNull;

import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.StringJoiner;
import java.util.function.BiConsumer;

import javax.annotation.Nullable;

import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Ascii;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;

import com.linecorp.armeria.common.Flags;
import com.linecorp.armeria.common.HttpData;
import com.linecorp.armeria.common.HttpHeaderNames;
import com.linecorp.armeria.common.HttpHeaders;
import com.linecorp.armeria.common.HttpHeadersBuilder;
import com.linecorp.armeria.common.HttpMethod;
import com.linecorp.armeria.common.HttpStatus;
import com.linecorp.armeria.common.RequestHeaders;
import com.linecorp.armeria.common.RequestHeadersBuilder;
import com.linecorp.armeria.common.ResponseHeaders;
import com.linecorp.armeria.common.ResponseHeadersBuilder;
import com.linecorp.armeria.server.ServerConfig;

import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.DefaultHeaders;
import io.netty.handler.codec.UnsupportedValueConverter;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http2.DefaultHttp2Headers;
import io.netty.handler.codec.http2.Http2Exception;
import io.netty.handler.codec.http2.Http2Headers;
import io.netty.handler.codec.http2.HttpConversionUtil;
import io.netty.handler.codec.http2.HttpConversionUtil.ExtensionHeaderNames;
import io.netty.util.AsciiString;
import io.netty.util.HashingStrategy;
import io.netty.util.internal.StringUtil;

/**
 * Provides various utility functions for internal use related with HTTP.
 *
 *
The conversion between HTTP/1 and HTTP/2 has been forked from Netty's {@link HttpConversionUtil}.
 */
public final class ArmeriaHttpUtil {

    // Forked from Netty 4.1.34 at 4921f62c8ab8205fd222439dcd1811760b05daf1

    /**
     * The default case-insensitive {@link AsciiString} hasher and comparator for HTTP/2 headers.
     */
    private static final HashingStrategy<AsciiString> HTTP2_HEADER_NAME_HASHER =
            new HashingStrategy<AsciiString>() {
                @Override
                public int hashCode(AsciiString o) {
                    return o.hashCode();
                }

                @Override
                public boolean equals(AsciiString a, AsciiString b) {
                    return a.contentEqualsIgnoreCase(b);
                }
            };

    /**
     * The default HTTP content-type charset.
     * See https://tools.ietf.org/html/rfc2616#section-3.7.1
     */
    public static final Charset HTTP_DEFAULT_CONTENT_CHARSET = StandardCharsets.ISO_8859_1;

    /**
     * The old {@code "keep-alive"} header which has been superceded by {@code "connection"}.
     */
    public static final AsciiString HEADER_NAME_KEEP_ALIVE = AsciiString.cached("keep-alive");

    /**
     * The old {@code "proxy-connection"} header which has been superceded by {@code "connection"}.
     */
    public static final AsciiString HEADER_NAME_PROXY_CONNECTION = AsciiString.cached("proxy-connection");

    private static final URI ROOT = URI.create("/");

    /**
     * The set of headers that should not be directly copied when converting headers from HTTP/1 to HTTP/2.
     */
    private static final CharSequenceMap HTTP_TO_HTTP2_HEADER_BLACKLIST = new CharSequenceMap();

    /**
     * The set of headers that should not be directly copied when converting headers from HTTP/2 to HTTP/1.
     */
    private static final CharSequenceMap HTTP2_TO_HTTP_HEADER_BLACKLIST = new CharSequenceMap();

    /**
     * The set of headers that must not be directly copied when converting trailers.
     */
    private static final CharSequenceMap HTTP_TRAILER_BLACKLIST = new CharSequenceMap();

    static {
        HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaderNames.CONNECTION, EMPTY_STRING);
        HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HEADER_NAME_KEEP_ALIVE, EMPTY_STRING);
        HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HEADER_NAME_PROXY_CONNECTION, EMPTY_STRING);
        HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaderNames.TRANSFER_ENCODING, EMPTY_STRING);
        HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaderNames.HOST, EMPTY_STRING);
        HTTP_TO_HTTP2_HEADER_BLACKLIST.add(HttpHeaderNames.UPGRADE, EMPTY_STRING);
        HTTP_TO_HTTP2_HEADER_BLACKLIST.add(ExtensionHeaderNames.STREAM_ID.text(), EMPTY_STRING);
        HTTP_TO_HTTP2_HEADER_BLACKLIST.add(ExtensionHeaderNames.SCHEME.text(), EMPTY_STRING);
        HTTP_TO_HTTP2_HEADER_BLACKLIST.add(ExtensionHeaderNames.PATH.text(), EMPTY_STRING);

        // https://tools.ietf.org/html/rfc7540#section-8.1.2.3
        HTTP2_TO_HTTP_HEADER_BLACKLIST.add(HttpHeaderNames.AUTHORITY, EMPTY_STRING);
        HTTP2_TO_HTTP_HEADER_BLACKLIST.add(HttpHeaderNames.METHOD, EMPTY_STRING);
        HTTP2_TO_HTTP_HEADER_BLACKLIST.add(HttpHeaderNames.PATH, EMPTY_STRING);
        HTTP2_TO_HTTP_HEADER_BLACKLIST.add(HttpHeaderNames.SCHEME, EMPTY_STRING);
        HTTP2_TO_HTTP_HEADER_BLACKLIST.add(HttpHeaderNames.STATUS, EMPTY_STRING);

        // https://tools.ietf.org/html/rfc7540#section-8.1
        // The "chunked" transfer encoding defined in Section 4.1 of [RFC7230] MUST NOT be used in HTTP/2.
        HTTP2_TO_HTTP_HEADER_BLACKLIST.add(HttpHeaderNames.TRANSFER_ENCODING, EMPTY_STRING);

        HTTP2_TO_HTTP_HEADER_BLACKLIST.add(ExtensionHeaderNames.STREAM_ID.text(), EMPTY_STRING);
        HTTP2_TO_HTTP_HEADER_BLACKLIST.add(ExtensionHeaderNames.SCHEME.text(), EMPTY_STRING);
        HTTP2_TO_HTTP_HEADER_BLACKLIST.add(ExtensionHeaderNames.PATH.text(), EMPTY_STRING);

        // https://tools.ietf.org/html/rfc7230#section-4.1.2
        // https://tools.ietf.org/html/rfc7540#section-8.1
        // A sender MUST NOT generate a trailer that contains a field necessary for message framing:
        HTTP_TRAILER_BLACKLIST.add(HttpHeaderNames.TRANSFER_ENCODING, EMPTY_STRING);
        HTTP_TRAILER_BLACKLIST.add(HttpHeaderNames.CONTENT_LENGTH, EMPTY_STRING);

        // for request modifiers:
        HTTP_TRAILER_BLACKLIST.add(HttpHeaderNames.CACHE_CONTROL, EMPTY_STRING);
        HTTP_TRAILER_BLACKLIST.add(HttpHeaderNames.EXPECT, EMPTY_STRING);
        HTTP_TRAILER_BLACKLIST.add(HttpHeaderNames.HOST, EMPTY_STRING);
        HTTP_TRAILER_BLACKLIST.add(HttpHeaderNames.MAX_FORWARDS, EMPTY_STRING);
        HTTP_TRAILER_BLACKLIST.add(HttpHeaderNames.PRAGMA, EMPTY_STRING);
        HTTP_TRAILER_BLACKLIST.add(HttpHeaderNames.RANGE, EMPTY_STRING);
        HTTP_TRAILER_BLACKLIST.add(HttpHeaderNames.TE, EMPTY_STRING);

        // for authentication:
        HTTP_TRAILER_BLACKLIST.add(HttpHeaderNames.WWW_AUTHENTICATE, EMPTY_STRING);
        HTTP_TRAILER_BLACKLIST.add(HttpHeaderNames.AUTHORIZATION, EMPTY_STRING);
        HTTP_TRAILER_BLACKLIST.add(HttpHeaderNames.PROXY_AUTHENTICATE, EMPTY_STRING);
        HTTP_TRAILER_BLACKLIST.add(HttpHeaderNames.PROXY_AUTHORIZATION, EMPTY_STRING);

        // for response control data:
        HTTP_TRAILER_BLACKLIST.add(HttpHeaderNames.DATE, EMPTY_STRING);
        HTTP_TRAILER_BLACKLIST.add(HttpHeaderNames.LOCATION, EMPTY_STRING);
        HTTP_TRAILER_BLACKLIST.add(HttpHeaderNames.RETRY_AFTER, EMPTY_STRING);
        HTTP_TRAILER_BLACKLIST.add(HttpHeaderNames.VARY, EMPTY_STRING);
        HTTP_TRAILER_BLACKLIST.add(HttpHeaderNames.WARNING, EMPTY_STRING);

        // or for determining how to process the payload:
        HTTP_TRAILER_BLACKLIST.add(HttpHeaderNames.CONTENT_ENCODING, EMPTY_STRING);
        HTTP_TRAILER_BLACKLIST.add(HttpHeaderNames.CONTENT_TYPE, EMPTY_STRING);
        HTTP_TRAILER_BLACKLIST.add(HttpHeaderNames.CONTENT_RANGE, EMPTY_STRING);
        HTTP_TRAILER_BLACKLIST.add(HttpHeaderNames.TRAILER, EMPTY_STRING);
    }

    /**
     * Translations from HTTP/2 header name to the HTTP/1.x equivalent. Currently, we expect these headers to
     * only allow a single value in the request. If adding headers that can potentially have multiple values,
     * please check the usage in code accordingly.
     */
    private static final CharSequenceMap REQUEST_HEADER_TRANSLATIONS = new CharSequenceMap();
    private static final CharSequenceMap RESPONSE_HEADER_TRANSLATIONS = new CharSequenceMap();

    static {
        RESPONSE_HEADER_TRANSLATIONS.add(Http2Headers.PseudoHeaderName.AUTHORITY.value(),
                                         HttpHeaderNames.HOST);
        REQUEST_HEADER_TRANSLATIONS.add(RESPONSE_HEADER_TRANSLATIONS);
    }

    /**
     * rfc7540, 8.1.2.3 states the path must not
     * be empty, and instead should be {@code /}.
     */
    private static final String EMPTY_REQUEST_PATH = "/";

    private static final Splitter COOKIE_SPLITTER = Splitter.on(';').trimResults().omitEmptyStrings();
    private static final String COOKIE_SEPARATOR = "; ";

    @Nullable
    private static final LoadingCache<AsciiString, String> HEADER_VALUE_CACHE =
            Flags.headerValueCacheSpec().map(ArmeriaHttpUtil::buildCache).orElse(null);
    private static final Set<AsciiString> CACHED_HEADERS = Flags.cachedHeaders().stream().map(AsciiString::of)
                                                                .collect(toImmutableSet());

    private static LoadingCache<AsciiString, String> buildCache(String spec) {
        return Caffeine.from(spec).build(AsciiString::toString);
    }

    /**
     * Concatenates two path strings.
     */
    public static String concatPaths(@Nullable String path1, @Nullable String path2) {
        path2 = path2 == null ? "" : path2;

        if (path1 == null || path1.isEmpty() || EMPTY_REQUEST_PATH.equals(path1)) {
            if (path2.isEmpty()) {
                return EMPTY_REQUEST_PATH;
            }

            if (path2.charAt(0) == '/') {
                return path2; // Most requests will land here.
            }

            return '/' + path2;
        }

        // At this point, we are sure path1 is neither empty nor null.
        if (path2.isEmpty()) {
            // Only path1 is non-empty. No need to concatenate.
            return path1;
        }

        if (path1.charAt(path1.length() - 1) == '/') {
            if (path2.charAt(0) == '/') {
                // path1 ends with '/' and path2 starts with '/'.
                // Avoid double-slash by stripping the first slash of path2.
                return new StringBuilder(path1.length() + path2.length() - 1)
                        .append(path1).append(path2, 1, path2.length()).toString();
            }

            // path1 ends with '/' and path2 does not start with '/'.
            // Simple concatenation would suffice.
            return path1 + path2;
        }

        if (path2.charAt(0) == '/') {
            // path1 does not end with '/' and path2 starts with '/'.
            // Simple concatenation would suffice.
            return path1 + path2;
        }

        // path1 does not end with '/' and path2 does not start with '/'.
        // Need to insert '/' between path1 and path2.
        return path1 + '/' + path2;
    }

    /**
     * Decodes a percent-encoded path string.
     */
    public static String decodePath(String path) {
        if (path.indexOf('%') < 0) {
            // No need to decoded; not percent-encoded
            return path;
        }

        // Decode percent-encoded characters.
        // An invalid character is replaced with 0xFF, which will be replaced into '�' by UTF-8 decoder.
        final int len = path.length();
        final byte[] buf = ThreadLocalByteArray.get(len);
        int dstLen = 0;
        for (int i = 0; i < len; i++) {
            final char ch = path.charAt(i);
            if (ch != '%') {
                buf[dstLen++] = (byte) ((ch & 0xFF80) == 0 ? ch : 0xFF);
                continue;
            }

            // Decode a percent-encoded character.
            final int hexEnd = i + 3;
            if (hexEnd > len) {
                // '%' or '%x' (must be followed by two hexadigits)
                buf[dstLen++] = (byte) 0xFF;
                break;
            }

            final int digit1 = decodeHexNibble(path.charAt(++i));
            final int digit2 = decodeHexNibble(path.charAt(++i));
            if (digit1 < 0 || digit2 < 0) {
                // The first or second digit is not hexadecimal.
                buf[dstLen++] = (byte) 0xFF;
            } else {
                buf[dstLen++] = (byte) ((digit1 << 4) | digit2);
            }
        }

        return new String(buf, 0, dstLen, StandardCharsets.UTF_8);
    }

    /**
     * Returns {@code true} if the specified {@code path} is an absolute {@code URI}.
     */
    public static boolean isAbsoluteUri(@Nullable String maybeUri) {
        if (maybeUri == null) {
            return false;
        }
        final int firstColonIdx = maybeUri.indexOf(':');
        if (firstColonIdx <= 0 || firstColonIdx + 3 >= maybeUri.length()) {
            return false;
        }
        final int firstSlashIdx = maybeUri.indexOf('/');
        if (firstSlashIdx <= 0 || firstSlashIdx < firstColonIdx) {
            return false;
        }

        return maybeUri.charAt(firstColonIdx + 1) == '/' && maybeUri.charAt(firstColonIdx + 2) == '/';
    }

    /**
     * Returns {@code true} if the specified HTTP status string represents an informational status.
     */
    public static boolean isInformational(@Nullable String statusText) {
        return statusText != null && !statusText.isEmpty() && statusText.charAt(0) == '1';
    }

    /**
     * Returns {@code true} if the content of the response with the given {@link HttpStatus} is expected to
     * be always empty (1xx, 204, 205 and 304 responses.)
     *
     * @throws IllegalArgumentException if the specified {@code content} or {@code trailers} are
     *                                  non-empty when the content is always empty
     */
    public static boolean isContentAlwaysEmptyWithValidation(
            HttpStatus status, HttpData content, HttpHeaders trailers) {
        if (!status.isContentAlwaysEmpty()) {
            return false;
        }

        if (!content.isEmpty()) {
            throw new IllegalArgumentException(
                    "A " + status + " response must have empty content: " + content.length() + " byte(s)");
        }
        if (!trailers.isEmpty()) {
            throw new IllegalArgumentException(
                    "A " + status + " response must not have trailers: " + trailers);
        }

        return true;
    }

    /**
     * Returns {@code true} if the specified {@code request} is a CORS preflight request.
     */
    public static boolean isCorsPreflightRequest(com.linecorp.armeria.common.HttpRequest request) {
        requireNonNull(request, "request");
        return request.method() == HttpMethod.OPTIONS &&
               request.headers().contains(HttpHeaderNames.ORIGIN) &&
               request.headers().contains(HttpHeaderNames.ACCESS_CONTROL_REQUEST_METHOD);
    }

    /**
     * Parses the specified HTTP header directives and invokes the specified {@code callback}
     * with the directive names and values.
     */
    public static void parseDirectives(String directives, BiConsumer<String, String> callback) {
        final int len = directives.length();
        for (int i = 0; i < len;) {
            final int nameStart = i;
            final String name;
            final String value;

            // Find the name.
            for (; i < len; i++) {
                final char ch = directives.charAt(i);
                if (ch == ',' || ch == '=') {
                    break;
                }
            }
            name = directives.substring(nameStart, i).trim();

            // Find the value.
            if (i == len || directives.charAt(i) == ',') {
                // Skip comma or go beyond 'len' to break the loop.
                i++;
                value = null;
            } else {
                // Skip '='.
                i++;

                // Skip whitespaces.
                for (; i < len; i++) {
                    final char ch = directives.charAt(i);
                    if (ch != ' ' && ch != '\t') {
                        break;
                    }
                }

                if (i < len && directives.charAt(i) == '\"') {
                    // Handle quoted string.
                    // Skip the opening quote.
                    i++;
                    final int valueStart = i;

                    // Find the closing quote.
                    for (; i < len; i++) {
                        if (directives.charAt(i) == '\"') {
                            break;
                        }
                    }
                    value = directives.substring(valueStart, i);

                    // Skip the closing quote.
                    i++;

                    // Find the comma and skip it.
                    for (; i < len; i++) {
                        if (directives.charAt(i) == ',') {
                            i++;
                            break;
                        }
                    }
                } else {
                    // Handle unquoted string.
                    final int valueStart = i;

                    // Find the comma.
                    for (; i < len; i++) {
                        if (directives.charAt(i) == ',') {
                            break;
                        }
                    }
                    value = directives.substring(valueStart, i).trim();

                    // Skip the comma.
                    i++;
                }
            }

            if (!name.isEmpty()) {
                callback.accept(Ascii.toLowerCase(name), Strings.emptyToNull(value));
            }
        }
    }

    /**
     * Converts the specified HTTP header directive value into a long integer.
     *
     * @return the converted value if {@code value} is equal to or greater than {@code 0}.
     *         {@code -1} otherwise, i.e. if a negative integer or not a number.
     */
    public static long parseDirectiveValueAsSeconds(@Nullable String value) {
        if (value == null) {
            return -1;
        }

        try {
            final long converted = Long.parseLong(value);
            return converted >= 0 ? converted : -1;
        } catch (NumberFormatException e) {
            return -1;
        }
    }

    /**
     * Converts the specified Netty HTTP/2 into Armeria HTTP/2 {@link RequestHeaders}.
     */
    public static RequestHeaders toArmeriaRequestHeaders(ChannelHandlerContext ctx, Http2Headers headers,
                                                         boolean endOfStream, String scheme,
                                                         ServerConfig cfg) {
        final RequestHeadersBuilder builder = RequestHeaders.builder();
        toArmeria(builder, headers, endOfStream);
        // A CONNECT request might not have ":scheme". See https://tools.ietf.org/html/rfc7540#section-8.1.2.3
        if (!builder.contains(HttpHeaderNames.SCHEME)) {
            builder.add(HttpHeaderNames.SCHEME, scheme);
        }
        if (!builder.contains(HttpHeaderNames.AUTHORITY)) {
            final String defaultHostname = cfg.defaultVirtualHost().defaultHostname();
            final int port = ((InetSocketAddress) ctx.channel().localAddress()).getPort();
            builder.add(HttpHeaderNames.AUTHORITY, defaultHostname + ':' + port);
        }
        return builder.build();
    }

    /**
     * Converts the specified Netty HTTP/2 into Armeria HTTP/2 headers.
     */
    public static HttpHeaders toArmeria(Http2Headers headers, boolean request, boolean endOfStream) {
        final HttpHeadersBuilder builder;
        if (request) {
            builder = headers.contains(HttpHeaderNames.METHOD) ? RequestHeaders.builder()
                                                               : HttpHeaders.builder();
        } else {
            builder = headers.contains(HttpHeaderNames.STATUS) ? ResponseHeaders.builder()
                                                               : HttpHeaders.builder();
        }

        toArmeria(builder, headers, endOfStream);
        return builder.build();
    }

    private static void toArmeria(HttpHeadersBuilder builder, Http2Headers headers, boolean endOfStream) {
        builder.sizeHint(headers.size());
        builder.endOfStream(endOfStream);

        StringJoiner cookieJoiner = null;
        for (Entry<CharSequence, CharSequence> e : headers) {
            final AsciiString name = HttpHeaderNames.of(e.getKey());
            final CharSequence value = e.getValue();

            // Cookies must be concatenated into a single octet string.
            // https://tools.ietf.org/html/rfc7540#section-8.1.2.5
            if (name.equals(HttpHeaderNames.COOKIE)) {
                if (cookieJoiner == null) {
                    cookieJoiner = new StringJoiner(COOKIE_SEPARATOR);
                }
                COOKIE_SPLITTER.split(value).forEach(cookieJoiner::add);
            } else {
                builder.add(name, convertHeaderValue(name, value));
            }
        }

        if (cookieJoiner != null && cookieJoiner.length() != 0) {
            builder.add(HttpHeaderNames.COOKIE, cookieJoiner.toString());
        }
    }

    /**
     * Converts the headers of the given Netty HTTP/1.x request into Armeria HTTP/2 headers.
     * The following headers are only used if they can not be found in the {@code HOST} header or the
     * {@code Request-Line} as defined by rfc7230
     *

     *
{@link ExtensionHeaderNames#SCHEME}

     *

     * {@link ExtensionHeaderNames#PATH} is ignored and instead extracted from the {@code Request-Line}.
     */
    public static RequestHeaders toArmeria(ChannelHandlerContext ctx, HttpRequest in,
                                           ServerConfig cfg) throws URISyntaxException {
        final URI requestTargetUri = toUri(in);

        final io.netty.handler.codec.http.HttpHeaders inHeaders = in.headers();
        final RequestHeadersBuilder out = RequestHeaders.builder();
        out.sizeHint(inHeaders.size());
        out.add(HttpHeaderNames.METHOD, in.method().name());
        out.add(HttpHeaderNames.PATH, toHttp2Path(requestTargetUri));

        addHttp2Scheme(inHeaders, requestTargetUri, out);

        if (!isOriginForm(requestTargetUri) && !isAsteriskForm(requestTargetUri)) {
            // Attempt to take from HOST header before taking from the request-line
            final String host = inHeaders.getAsString(HttpHeaderNames.HOST);
            addHttp2Authority(host == null || host.isEmpty() ? requestTargetUri.getAuthority() : host, out);
        }

        if (out.authority() == null) {
            final String defaultHostname = cfg.defaultVirtualHost().defaultHostname();
            final int port = ((InetSocketAddress) ctx.channel().localAddress()).getPort();
            out.add(HttpHeaderNames.AUTHORITY, defaultHostname + ':' + port);
        }

        // Add the HTTP headers which have not been consumed above
        toArmeria(inHeaders, out);
        return out.build();
    }

    /**
     * Converts the headers of the given Netty HTTP/1.x response into Armeria HTTP/2 headers.
     */
    public static ResponseHeaders toArmeria(HttpResponse in) {
        final io.netty.handler.codec.http.HttpHeaders inHeaders = in.headers();
        final ResponseHeadersBuilder out = ResponseHeaders.builder();
        out.sizeHint(inHeaders.size());
        out.add(HttpHeaderNames.STATUS, HttpStatus.valueOf(in.status().code()).codeAsText());
        // Add the HTTP headers which have not been consumed above
        toArmeria(inHeaders, out);
        return out.build();
    }

    /**
     * Converts the specified Netty HTTP/1 headers into Armeria HTTP/2 headers.
     */
    public static HttpHeaders toArmeria(io.netty.handler.codec.http.HttpHeaders inHeaders) {
        if (inHeaders.isEmpty()) {
            return HttpHeaders.of();
        }

        final HttpHeadersBuilder out = HttpHeaders.builder();
        out.sizeHint(inHeaders.size());
        toArmeria(inHeaders, out);
        return out.build();
    }

    /**
     * Converts the specified Netty HTTP/1 headers into Armeria HTTP/2 headers.
     */
    public static void toArmeria(io.netty.handler.codec.http.HttpHeaders inHeaders, HttpHeadersBuilder out) {
        final Iterator<Entry<CharSequence, CharSequence>> iter = inHeaders.iteratorCharSequence();
        // Choose 8 as a default size because it is unlikely we will see more than 4 Connection headers values,
        // but still allowing for "enough" space in the map to reduce the chance of hash code collision.
        final CharSequenceMap connectionBlacklist =
                toLowercaseMap(inHeaders.valueCharSequenceIterator(HttpHeaderNames.CONNECTION), 8);
        StringJoiner cookieJoiner = null;
        while (iter.hasNext()) {
            final Entry<CharSequence, CharSequence> entry = iter.next();
            final AsciiString aName = HttpHeaderNames.of(entry.getKey()).toLowerCase();
            if (HTTP_TO_HTTP2_HEADER_BLACKLIST.contains(aName) || connectionBlacklist.contains(aName)) {
                continue;
            }

            // https://tools.ietf.org/html/rfc7540#section-8.1.2.2 makes a special exception for TE
            if (aName.equals(HttpHeaderNames.TE)) {
                toHttp2HeadersFilterTE(entry, out);
                continue;
            }

            // Cookies must be concatenated into a single octet string.
            // https://tools.ietf.org/html/rfc7540#section-8.1.2.5
            final CharSequence value = entry.getValue();
            if (aName.equals(HttpHeaderNames.COOKIE)) {
                if (cookieJoiner == null) {
                    cookieJoiner = new StringJoiner(COOKIE_SEPARATOR);
                }
                COOKIE_SPLITTER.split(value).forEach(cookieJoiner::add);
            } else {
                out.add(aName, convertHeaderValue(aName, value));
            }
        }

        if (cookieJoiner != null && cookieJoiner.length() != 0) {
            out.add(HttpHeaderNames.COOKIE, cookieJoiner.toString());
        }
    }

    private static CharSequenceMap toLowercaseMap(Iterator<? extends CharSequence> valuesIter,
                                                  int arraySizeHint) {
        final CharSequenceMap result = new CharSequenceMap(arraySizeHint);

        while (valuesIter.hasNext()) {
            final AsciiString lowerCased = HttpHeaderNames.of(valuesIter.next()).toLowerCase();
            try {
                int index = lowerCased.forEachByte(FIND_COMMA);
                if (index != -1) {
                    int start = 0;
                    do {
                        result.add(lowerCased.subSequence(start, index, false).trim(), EMPTY_STRING);
                        start = index + 1;
                    } while (start < lowerCased.length() &&
                             (index = lowerCased.forEachByte(start,
                                                             lowerCased.length() - start, FIND_COMMA)) != -1);
                    result.add(lowerCased.subSequence(start, lowerCased.length(), false).trim(), EMPTY_STRING);
                } else {
                    result.add(lowerCased.trim(), EMPTY_STRING);
                }
            } catch (Exception e) {
                // This is not expect to happen because FIND_COMMA never throws but must be caught
                // because of the ByteProcessor interface.
                throw new IllegalStateException(e);
            }
        }
        return result;
    }

    /**
     * Filter the {@link HttpHeaderNames#TE} header according to the
     * special rules in the HTTP/2 RFC.
     * @param entry An entry whose name is {@link HttpHeaderNames#TE}.
     * @param out the resulting HTTP/2 headers.
     */
    private static void toHttp2HeadersFilterTE(Entry<CharSequence, CharSequence> entry,
                                               HttpHeadersBuilder out) {
        if (AsciiString.indexOf(entry.getValue(), ',', 0) == -1) {
            if (AsciiString.contentEqualsIgnoreCase(AsciiString.trim(entry.getValue()),
                                                    HttpHeaderValues.TRAILERS)) {
                out.add(HttpHeaderNames.TE, HttpHeaderValues.TRAILERS.toString());
            }
        } else {
            final List<CharSequence> teValues = StringUtil.unescapeCsvFields(entry.getValue());
            for (CharSequence teValue : teValues) {
                if (AsciiString.contentEqualsIgnoreCase(AsciiString.trim(teValue),
                                                        HttpHeaderValues.TRAILERS)) {
                    out.add(HttpHeaderNames.TE, HttpHeaderValues.TRAILERS.toString());
                    break;
                }
            }
        }
    }

    private static URI toUri(HttpRequest in) throws URISyntaxException {
        final String uri = in.uri();
        if (uri.startsWith("//")) {
            // Normalize the path that starts with more than one slash into the one with a single slash,
            // so that java.net.URI does not raise a URISyntaxException.
            for (int i = 0; i < uri.length(); i++) {
                if (uri.charAt(i) != '/') {
                    return new URI(uri.substring(i - 1));
                }
            }
            return ROOT;
        } else {
            return new URI(uri);
        }
    }

    /**
     * Generate a HTTP/2 {code :path} from a URI in accordance with
     * rfc7230, 5.3.
     */
    private static String toHttp2Path(URI uri) {
        final StringBuilder pathBuilder = new StringBuilder(
                length(uri.getRawPath()) + length(uri.getRawQuery()) + length(uri.getRawFragment()) + 2);

        if (!isNullOrEmpty(uri.getRawPath())) {
            pathBuilder.append(uri.getRawPath());
        }
        if (!isNullOrEmpty(uri.getRawQuery())) {
            pathBuilder.append('?');
            pathBuilder.append(uri.getRawQuery());
        }
        if (!isNullOrEmpty(uri.getRawFragment())) {
            pathBuilder.append('#');
            pathBuilder.append(uri.getRawFragment());
        }

        return pathBuilder.length() != 0 ? pathBuilder.toString() : EMPTY_REQUEST_PATH;
    }

    @VisibleForTesting
    static void addHttp2Authority(@Nullable String authority, RequestHeadersBuilder out) {
        // The authority MUST NOT include the deprecated "userinfo" subcomponent
        if (authority != null) {
            final String actualAuthority;
            if (authority.isEmpty()) {
                actualAuthority = "";
            } else {
                final int start = authority.indexOf('@') + 1;
                if (start == 0) {
                    actualAuthority = authority;
                } else if (authority.length() == start) {
                    throw new IllegalArgumentException("authority: " + authority);
                } else {
                    actualAuthority = authority.substring(start);
                }
            }
            out.add(HttpHeaderNames.AUTHORITY, actualAuthority);
        }
    }

    private static void addHttp2Scheme(io.netty.handler.codec.http.HttpHeaders in, URI uri,
                                       RequestHeadersBuilder out) {
        final String value = uri.getScheme();
        if (value != null) {
            out.add(HttpHeaderNames.SCHEME, value);
            return;
        }

        // Consume the Scheme extension header if present
        final CharSequence cValue = in.get(ExtensionHeaderNames.SCHEME.text());
        if (cValue != null) {
            out.add(HttpHeaderNames.SCHEME, cValue.toString());
        } else {
            out.add(HttpHeaderNames.SCHEME, "unknown");
        }
    }

    /**
     * Converts the specified Armeria HTTP/2 headers into Netty HTTP/2 headers.
     */
    public static Http2Headers toNettyHttp2(HttpHeaders in, boolean server) {
        final Http2Headers out = new DefaultHttp2Headers(false, in.size());

        // Trailers if it does not have :status.
        if (server && !in.contains(HttpHeaderNames.STATUS)) {
            for (Entry<AsciiString, String> entry : in) {
                final AsciiString name = entry.getKey();
                final String value = entry.getValue();
                if (name.isEmpty() || isTrailerBlacklisted(name)) {
                    continue;
                }
                out.add(name, value);
            }
        } else {
            in.forEach((BiConsumer<AsciiString, String>) out::add);
            out.remove(HttpHeaderNames.CONNECTION);
            out.remove(HttpHeaderNames.TRANSFER_ENCODING);
        }

        if (!out.contains(HttpHeaderNames.COOKIE)) {
            return out;
        }

        // Split up cookies to allow for better compression.
        // https://tools.ietf.org/html/rfc7540#section-8.1.2.5
        final List<CharSequence> cookies = out.getAllAndRemove(HttpHeaderNames.COOKIE);
        for (CharSequence c : cookies) {
            out.add(HttpHeaderNames.COOKIE, COOKIE_SPLITTER.split(c));
        }

        return out;
    }

    /**
     * Translate and add HTTP/2 headers to HTTP/1.x headers.
     *
     * @param streamId The stream associated with {@code sourceHeaders}.
     * @param inputHeaders The HTTP/2 headers to convert.
     * @param outputHeaders The object which will contain the resulting HTTP/1.x headers..
     * @param httpVersion What HTTP/1.x version {@code outputHeaders} should be treated as
     *                    when doing the conversion.
     * @param isTrailer {@code true} if {@code outputHeaders} should be treated as trailers.
     *                  {@code false} otherwise.
     * @param isRequest {@code true} if the {@code outputHeaders} will be used in a request message.
     *                  {@code false} for response message.
     *
     * @throws Http2Exception If not all HTTP/2 headers can be translated to HTTP/1.x.
     */
    public static void toNettyHttp1(
            int streamId, HttpHeaders inputHeaders, io.netty.handler.codec.http.HttpHeaders outputHeaders,
            HttpVersion httpVersion, boolean isTrailer, boolean isRequest) throws Http2Exception {

        final CharSequenceMap translations = isRequest ? REQUEST_HEADER_TRANSLATIONS
                                                       : RESPONSE_HEADER_TRANSLATIONS;
        StringJoiner cookieJoiner = null;
        try {
            for (Entry<AsciiString, String> entry : inputHeaders) {
                final AsciiString name = entry.getKey();
                final String value = entry.getValue();
                final AsciiString translatedName = translations.get(name);
                if (translatedName != null && !inputHeaders.contains(translatedName)) {
                    outputHeaders.add(translatedName, value);
                    continue;
                }

                if (name.isEmpty() || HTTP2_TO_HTTP_HEADER_BLACKLIST.contains(name)) {
                    continue;
                }

                if (isTrailer && isTrailerBlacklisted(name)) {
                    continue;
                }

                if (HttpHeaderNames.COOKIE.equals(name)) {
                    // combine the cookie values into 1 header entry.
                    // https://tools.ietf.org/html/rfc7540#section-8.1.2.5
                    if (cookieJoiner == null) {
                        cookieJoiner = new StringJoiner(COOKIE_SEPARATOR);
                    }
                    COOKIE_SPLITTER.split(value).forEach(cookieJoiner::add);
                } else {
                    outputHeaders.add(name, value);
                }
            }

            if (cookieJoiner != null && cookieJoiner.length() != 0) {
                outputHeaders.add(HttpHeaderNames.COOKIE, cookieJoiner.toString());
            }
        } catch (Throwable t) {
            throw streamError(streamId, PROTOCOL_ERROR, t, "HTTP/2 to HTTP/1.x headers conversion error");
        }

        if (!isTrailer) {
            HttpUtil.setKeepAlive(outputHeaders, httpVersion, true);
        }
    }

    /**
     * Returns a {@link ResponseHeaders} whose {@link HttpHeaderNames#CONTENT_LENGTH} is added or removed
     * according to the status of the specified {@code headers}, {@code content} and {@code trailers}.
     * The {@link HttpHeaderNames#CONTENT_LENGTH} is removed when:
     *

     *
the status of the specified {@code headers} is one of informational headers,
     *   {@link HttpStatus#NO_CONTENT} or {@link HttpStatus#RESET_CONTENT}

     *
the trailers exists

     *

     * The {@link HttpHeaderNames#CONTENT_LENGTH} is added when the state of the specified {@code headers}
     * does not meet the conditions above and {@link HttpHeaderNames#CONTENT_LENGTH} is not present
     * regardless of the fact that the content is empty or not.
     *
     * @throws IllegalArgumentException if the specified {@code content} or {@code trailers} are
     *                                  non-empty when the content is always empty
     */
    public static ResponseHeaders setOrRemoveContentLength(ResponseHeaders headers, HttpData content,
                                                           HttpHeaders trailers) {
        requireNonNull(headers, "headers");
        requireNonNull(content, "content");
        requireNonNull(trailers, "trailers");

        final HttpStatus status = headers.status();

        if (isContentAlwaysEmptyWithValidation(status, content, trailers)) {
            if (status != HttpStatus.NOT_MODIFIED) {
                if (headers.contains(HttpHeaderNames.CONTENT_LENGTH)) {
                    final ResponseHeadersBuilder builder = headers.toBuilder();
                    builder.remove(HttpHeaderNames.CONTENT_LENGTH);
                    return builder.build();
                }
            } else {
                // 304 response can have the "content-length" header when it is a response to a conditional
                // GET request. See https://tools.ietf.org/html/rfc7230#section-3.3.2
            }

            return headers;
        }

        if (!trailers.isEmpty()) {
            // Some of the client implementations such as "curl" ignores trailers if
            // the "content-length" header is present. We should not set "content-length" header when
            // trailers exists so that those clients can receive the trailers.
            // The response is sent using chunked transfer encoding in HTTP/1 or a DATA frame payload
            // in HTTP/2, so it's no worry.
            if (headers.contains(HttpHeaderNames.CONTENT_LENGTH)) {
                final ResponseHeadersBuilder builder = headers.toBuilder();
                builder.remove(HttpHeaderNames.CONTENT_LENGTH);
                return builder.build();
            }

            return headers;
        }

        if (!headers.contains(HttpHeaderNames.CONTENT_LENGTH) || !content.isEmpty()) {
            return headers.toBuilder()
                          .setInt(HttpHeaderNames.CONTENT_LENGTH, content.length())
                          .build();
        }

        // The header contains "content-length" header and the content is empty.
        // Do not overwrite the header because a response to a HEAD request
        // will have no content even if it has non-zero content-length header.
        return headers;
    }

    private static String convertHeaderValue(AsciiString name, CharSequence value) {
        if (!(value instanceof AsciiString)) {
            return value.toString();
        }
        if (HEADER_VALUE_CACHE != null && CACHED_HEADERS.contains(name)) {
            final String converted = HEADER_VALUE_CACHE.get((AsciiString) value);
            assert converted != null; // loader does not return null.
            return converted;
        }
        return value.toString();
    }

    /**
     * Returns {@code true} if the specified header name is not allowed for HTTP tailers.
     */
    public static boolean isTrailerBlacklisted(AsciiString name) {
        return HTTP_TRAILER_BLACKLIST.contains(name);
    }

    private static final class CharSequenceMap
            extends DefaultHeaders<AsciiString, AsciiString, CharSequenceMap> {

        CharSequenceMap() {
            super(HTTP2_HEADER_NAME_HASHER, UnsupportedValueConverter.instance());
        }

        @SuppressWarnings("unchecked")
        CharSequenceMap(int size) {
            super(HTTP2_HEADER_NAME_HASHER, UnsupportedValueConverter.instance(), NameValidator.NOT_NULL, size);
        }
    }

    private ArmeriaHttpUtil() {}
}

Output:
【漏洞类型】HTTP 头部注入漏洞
【严重性】High
【漏洞摘要】这是由于未对用户输入的 HTTP 头部名称和值进行严格的验证导致的。在 `HttpHeaderNames.of()` 方法中，用户可以输入包含控制字符的头部名称（例如，`"my-header\nX-Frame-Options: DENY"`），从而在响应中注入额外的头部或任意内容。同样，在 `HttpHeadersBase` 类中，用户可以输入包含控制字符的头部值，这可能导致响应的不正确格式或安全问题。
【关键代码模式】
- `HttpHeaderNames.of(CharSequence name)`
- `HttpHeadersBase.add(CharSequence name, String value)`
- `HttpHeadersBase.add(CharSequence name, Iterable<String> values)`
- `HttpHeadersBase.add(CharSequence name, String... values)`
- `HttpHeadersBase.set(CharSequence name, String value)`
- `HttpHeadersBase.set(CharSequence name, Iterable<String> values)`
- `HttpHeadersBase.set(CharSequence name, String... values)`
- `HttpHeadersBase.setObject(CharSequence name, Object value)`
- `HttpHeadersBase.setObject(CharSequence name, Iterable<?> values)`
- `HttpHeadersBase.setObject(CharSequence name, Object... values)`
- `HttpHeadersBase.setObject(Iterable<? extends Entry<? extends CharSequence, ?>> headers)`
- `AsciiString.toLowerCase`
- `AsciiString.hashCode`
- `AsciiString.contentEquals`
【修复方法说明】修复方法通过在 `HttpHeaderNames.of()` 和 `HttpHeadersBase` 类中添加严格的头部名称和值验证来解决问题。使用 `BitSet` 来快速检查和拒绝包含控制字符的头部名称和值。这确保了只有合法的头部名称和值才能被添加到 HTTP 响应中，从而防止头部注入攻击。