HttpHeaders
HttpHeaders 是用于存储和操作HTTP请求或响应头部字段的接口。
// DefaultHttpHeaders, HttpHeadersFactory.TrailingHttpHeaders
public interface HttpHeaders extends Iterable<Entry<CharSequence, CharSequence>> {static HttpHeaders emptyHeaders() {return newHeaders(2, true, true, true);}static HttpHeaders newHeaders() {return newHeaders(true);}static HttpHeaders newHeaders(boolean validate) {return newHeaders(16, validate, validate, validate);}static HttpHeaders newHeaders(int sizeHint, boolean checkNames, boolean checkCookies, boolean checkValues) {return new DefaultHttpHeaders(sizeHint, checkNames, checkCookies, checkValues);}HttpHeaders copy();@NullableCharSequence get(CharSequence name);default CharSequence get(final CharSequence name, final CharSequence defaultValue) {final CharSequence value = get(name);return value != null ? value : defaultValue;}@NullableCharSequence getAndRemove(CharSequence name);default CharSequence getAndRemove(final CharSequence name, final CharSequence defaultValue) {final CharSequence value = getAndRemove(name);return value == null ? defaultValue : value;}Iterator<CharSequence> valuesIterator(CharSequence name);default Iterable<CharSequence> values(CharSequence name) {return () -> (Iterator<CharSequence>) valuesIterator(name);}default boolean contains(final CharSequence name) {return get(name) != null;}default boolean contains(CharSequence name, CharSequence value) {return AsciiString.contentEquals(get(name), value);}default boolean containsIgnoreCase(CharSequence name, CharSequence value) {return AsciiString.contentEqualsIgnoreCase(get(name), value);}int size();default boolean isEmpty() {return size() == 0;}Set<CharSequence> names();HttpHeaders add(CharSequence name, CharSequence value);HttpHeaders add(CharSequence name, Iterable<? extends CharSequence> values);default HttpHeaders add(CharSequence name, Iterator<? extends CharSequence> valuesItr) {while (valuesItr.hasNext()) {add(name, valuesItr.next());}return this;}HttpHeaders add(CharSequence name, CharSequence... values);HttpHeaders add(HttpHeaders headers);HttpHeaders set(CharSequence name, CharSequence value);HttpHeaders set(CharSequence name, Iterable<? extends CharSequence> values);default HttpHeaders set(CharSequence name, Iterator<? extends CharSequence> valueItr) {remove(name);while (valueItr.hasNext()) {add(name, valueItr.next());}return this;}default HttpHeaders set(CharSequence name, CharSequence... values) {remove(name);for (CharSequence value : values) {add(name, value);}return this;}default HttpHeaders set(final HttpHeaders headers) {if (headers != this) {clear();add(headers);}return this;}default HttpHeaders replace(final HttpHeaders headers) {if (headers != this) {for (final CharSequence key : headers.names()) {remove(key);}add(headers);}return this;}boolean remove(CharSequence name);boolean remove(CharSequence name, CharSequence value);boolean removeIgnoreCase(CharSequence name, CharSequence value);HttpHeaders clear();@OverrideIterator<Entry<CharSequence, CharSequence>> iterator();@Overridedefault Spliterator<Entry<CharSequence, CharSequence>> spliterator() {return Spliterators.spliterator(iterator(), size(), Spliterator.SIZED);}@OverrideString toString();default String toString(BiFunction<? super CharSequence, ? super CharSequence, CharSequence> filter) {return HeaderUtils.toString(this, filter);}@NullableHttpCookiePair getCookie(CharSequence name);@NullableHttpSetCookie getSetCookie(CharSequence name);default Iterable<HttpCookiePair> getCookies() {return () -> (Iterator<HttpCookiePair>) getCookiesIterator();}Iterator<HttpCookiePair> getCookiesIterator();default Iterable<HttpCookiePair> getCookies(CharSequence name) {return () -> (Iterator<HttpCookiePair>) getCookiesIterator(name);}Iterator<HttpCookiePair> getCookiesIterator(CharSequence name);default Iterable<HttpSetCookie> getSetCookies() {return () -> (Iterator<HttpSetCookie>) getSetCookiesIterator();}Iterator<HttpSetCookie> getSetCookiesIterator();default Iterable<HttpSetCookie> getSetCookies(CharSequence name) {return () -> (Iterator<HttpSetCookie>) getSetCookiesIterator(name);}Iterator<HttpSetCookie> getSetCookiesIterator(CharSequence name);default Iterable<HttpSetCookie> getSetCookies(CharSequence name, CharSequence domain, CharSequence path) {return () -> (Iterator<HttpSetCookie>) getSetCookiesIterator(name, domain, path);}Iterator<HttpSetCookie> getSetCookiesIterator(CharSequence name, CharSequence domain, CharSequence path);HttpHeaders addCookie(HttpCookiePair cookie);default HttpHeaders addCookie(final CharSequence name, final CharSequence value) {return addCookie(new DefaultHttpCookiePair(name, value));}HttpHeaders addSetCookie(HttpSetCookie cookie);default HttpHeaders addSetCookie(final CharSequence name, final CharSequence value) {return addSetCookie(new DefaultHttpSetCookie(name, value));}boolean removeCookies(CharSequence name);boolean removeSetCookies(CharSequence name);boolean removeSetCookies(CharSequence name, CharSequence domain, CharSequence path);
}
HttpCookiePair
HttpCookiePair 接口定义了 HTTP Cookie 键值对的结构,包括名称、值、是否被双引号包裹及其编码表示, 格式如下:
- <cookie-name>=<cookie-value>
- <cookie-name>=“<cookie-value>”
// DefaultHttpCookiePair
public interface HttpCookiePair {CharSequence name();CharSequence value();boolean isWrapped();CharSequence encodedCookie();
}
HttpSetCookie
HttpSetCookie 接口扩展了 HttpCookiePair,表示一个完整的 Set-Cookie,包含域、路径、生命周期、安全属性、SameSite 策略等信息,支持编码与过期计算。
Set-Cookie HTTP 头的格式大致如下:
Set-Cookie: <cookie-name>=<cookie-value>; Expires=<date>; Max-Age=<seconds>; Domain=<domain>; Path=<path>; Secure; HttpOnly; SameSite=<Lax|Strict|None>; Partitioned
字段名 | 是否必填 | 值类型 | 说明 |
---|---|---|---|
name=value | 是 | 字符串 | Cookie 的名称和值,必须出现在首位。value 可包含特殊字符,建议进行 URL 编码。 |
Expires=<date> | 否 | GMT 日期字符串 | 设定 Cookie 的过期时间,格式如 Wed, 09 Jun 2027 10:18:14 GMT 。过期即被删除。 |
Max-Age=<秒数> | 否 | 整数(秒) | 设置 Cookie 的生存时间(从当前起多少秒),优先级高于 Expires 。 |
Domain=<domain> | 否 | 字符串 | 指定 Cookie 可被哪些域访问,默认是当前域,不带子域;设置 .example.com 可包括子域。 |
Path=<path> | 否 | 字符串 | 指定 Cookie 生效的路径,默认是当前路径及其子路径。常用 / 表示全站有效。 |
Secure | 否 | 无值 | 表示仅在 HTTPS 连接中发送该 Cookie,保障传输安全。 |
HttpOnly | 否 | 无值 | 禁止通过 JavaScript 访问该 Cookie,可防范 XSS 攻击。 |
SameSite | 否 | Lax | Strict | None | 控制是否允许跨站点请求携带该 Cookie。 • Strict : 严格禁止跨站请求• Lax : 允许部分(如 GET 跳转)• None : 允许全部,需配合 Secure |
Partitioned | 否 | 无值 | 指示该 Cookie 为分区 Cookie(Partitioned Cookie)。必须与 Secure 和 SameSite=None 一起使用,仅部分浏览器支持。 |
// DefaultHttpSetCookie
public interface HttpSetCookie extends HttpCookiePair {@NullableCharSequence domain();@NullableCharSequence path();@NullableLong maxAge();@NullableCharSequence expires();@Nullabledefault Long expiresAsMaxAge() {CharSequence expires = expires();if (expires != null) {Date expiresDate = DateFormatter.parseHttpDate(expires);if (expiresDate != null) {long maxAgeMillis = expiresDate.getTime() - System.currentTimeMillis();return maxAgeMillis / 1000 + (maxAgeMillis % 1000 != 0 ? 1 : 0);}}return null;}@NullableSameSite sameSite();boolean isSecure();boolean isHttpOnly();CharSequence encodedSetCookie();enum SameSite {Lax, Strict, None}boolean isPartitioned();
}
HttpHeadersFactory
HttpHeadersFactory 是用于创建和配置 HTTP 头部对象及其校验行为的工厂接口。
// DefaultHttpHeadersFactory
public interface HttpHeadersFactory {HttpHeaders newHeaders();HttpHeaders newEmptyHeaders();int getSizeHint();boolean isValidatingNames();boolean isValidatingValues();boolean isValidatingCookies();
}
public final class DefaultHttpHeadersFactory implements HttpHeadersFactory {private static final int SIZE_HINT = 16;private static final DefaultHttpHeadersFactory FOR_HEADER =new DefaultHttpHeadersFactory(SIZE_HINT, true, true, true, false);private static final DefaultHttpHeadersFactory FOR_TRAILER =new DefaultHttpHeadersFactory(SIZE_HINT, true, true, true, true);private static final int MIN_SIZE_HINT = 2;// 用于提示内部数据结构(通常是哈希表)应该多大。private final int sizeHint;// 是否开启 HTTP 头名称的合法性校验(比如是否符合 RFC 规定的字符集等)。private final boolean validateNames;// 是否开启 HTTP 头的值的合法性校验。private final boolean validateValues;// 是否在解析 Cookie 时对 Cookie 内容做合法性校验(如格式、字符等)。private final boolean validateCookies;// 是否以“尾部头”(trailer header)专用的规则进行名称校验。private final boolean validateAsTrailer;private DefaultHttpHeadersFactory(int sizeHint, boolean validateNames, boolean validateValues, boolean validateCookies, boolean validateAsTrailer) {this.sizeHint = Math.max(MIN_SIZE_HINT, sizeHint); this.validateNames = validateNames;this.validateValues = validateValues;this.validateCookies = validateCookies;this.validateAsTrailer = validateAsTrailer;}public static DefaultHttpHeadersFactory headersFactory() {return FOR_HEADER;}public static DefaultHttpHeadersFactory trailersFactory() {return FOR_TRAILER;}@Overridepublic HttpHeaders newHeaders() {if (validateAsTrailer) {return new TrailingHttpHeaders(sizeHint, validateNames, validateCookies, validateValues);}return HttpHeaders.newHeaders(sizeHint, validateNames, validateCookies, validateValues);}@Overridepublic HttpHeaders newEmptyHeaders() {if (validateAsTrailer) {return new TrailingHttpHeaders(MIN_SIZE_HINT, validateNames, validateCookies, validateValues);}return HttpHeaders.newHeaders(MIN_SIZE_HINT, validateNames, validateCookies, validateValues);}// ...private static final class TrailingHttpHeaders extends DefaultHttpHeaders {TrailingHttpHeaders(int arraySizeHint, boolean validateNames, boolean validateCookies, boolean validateValues) {super(arraySizeHint, validateNames, validateCookies, validateValues);}// 该类用于处理 HTTP 的 trailing headers —— 即 chunked 编码中的最后一块 trailer 部分。// RFC 7230 明确禁止 trailing headers 不能包含某些首部字段,如:Content-Length, Transfer-Encoding, Trailer@Overrideprotected CharSequence validateKey(@Nullable CharSequence name, boolean forAdd) {if (HttpHeaderNames.CONTENT_LENGTH.contentEqualsIgnoreCase(name)|| HttpHeaderNames.TRANSFER_ENCODING.contentEqualsIgnoreCase(name)|| HttpHeaderNames.TRAILER.contentEqualsIgnoreCase(name)) {throw new IllegalArgumentException("Prohibited trailing header: " + name);}return super.validateKey(name, forAdd);}}
}