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();
@Nullable
CharSequence get(CharSequence name);
default CharSequence get(final CharSequence name, final CharSequence defaultValue) {
final CharSequence value = get(name);
return value != null ? value : defaultValue;
}
@Nullable
CharSequence 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();
@Override
Iterator<Entry<CharSequence, CharSequence>> iterator();
@Override
default Spliterator<Entry<CharSequence, CharSequence>> spliterator() {
return Spliterators.spliterator(iterator(), size(), Spliterator.SIZED);
}
@Override
String toString();
default String toString(BiFunction<? super CharSequence, ? super CharSequence, CharSequence> filter) {
return HeaderUtils.toString(this, filter);
}
@Nullable
HttpCookiePair getCookie(CharSequence name);
@Nullable
HttpSetCookie 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 {
@Nullable
CharSequence domain();
@Nullable
CharSequence path();
@Nullable
Long maxAge();
@Nullable
CharSequence expires();
@Nullable
default 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;
}
@Nullable
SameSite 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;
}
@Override
public HttpHeaders newHeaders() {
if (validateAsTrailer) {
return new TrailingHttpHeaders(sizeHint, validateNames, validateCookies, validateValues);
}
return HttpHeaders.newHeaders(sizeHint, validateNames, validateCookies, validateValues);
}
@Override
public 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
@Override
protected 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);
}
}
}