All Downloads are FREE. Search and download functionalities are using the official Maven repository.

top.jfunc.http.base.HttpHeaders Maven / Gradle / Ivy

/*
 * Copyright 2002-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package top.jfunc.http.base;

import java.net.URI;
import java.nio.charset.Charset;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import top.jfunc.common.utils.Joiner;
import top.jfunc.common.utils.LinkedMultiValueMap;
import top.jfunc.common.utils.StrUtil;

/**
 * 从spring挪过来,代表请求或者响应的headers
 * Represents HTTP request and response headers, mapping string header names to a list of string values.
 * @author Arjen Poutsma
 * @author Sebastien Deleuze
 * @author Brian Clozel
 * @author Juergen Hoeller
 * @author Josh Long
 * @since 3.0
 */
public class HttpHeaders extends LinkedMultiValueMap {
	/**
	 * The HTTP {@code Accept} header field name.
	 * @see Section 5.3.2 of RFC 7231
	 */
	public static final String ACCEPT = "Accept";
	/**
	 * The HTTP {@code Accept-Charset} header field name.
	 * @see Section 5.3.3 of RFC 7231
	 */
	public static final String ACCEPT_CHARSET = "Accept-Charset";
	public static final String CHARSET = "Charset";
	/**
	 * The HTTP {@code Accept-Encoding} header field name.
	 * @see Section 5.3.4 of RFC 7231
	 */
	public static final String ACCEPT_ENCODING = "Accept-Encoding";
	/**
	 * The HTTP {@code Accept-Language} header field name.
	 * @see Section 5.3.5 of RFC 7231
	 */
	public static final String ACCEPT_LANGUAGE = "Accept-Language";
	/**
	 * The HTTP {@code Accept-Ranges} header field name.
	 * @see Section 5.3.5 of RFC 7233
	 */
	public static final String ACCEPT_RANGES = "Accept-Ranges";
	/**
	 * The CORS {@code Access-Control-Allow-Credentials} response header field name.
	 * @see CORS W3C recommendation
	 */
	public static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials";
	/**
	 * The CORS {@code Access-Control-Allow-Headers} response header field name.
	 * @see CORS W3C recommendation
	 */
	public static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";
	/**
	 * The CORS {@code Access-Control-Allow-Methods} response header field name.
	 * @see CORS W3C recommendation
	 */
	public static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";
	/**
	 * The CORS {@code Access-Control-Allow-Origin} response header field name.
	 * @see CORS W3C recommendation
	 */
	public static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
	/**
	 * The CORS {@code Access-Control-Expose-Headers} response header field name.
	 * @see CORS W3C recommendation
	 */
	public static final String ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers";
	/**
	 * The CORS {@code Access-Control-Max-Age} response header field name.
	 * @see CORS W3C recommendation
	 */
	public static final String ACCESS_CONTROL_MAX_AGE = "Access-Control-Max-Age";
	/**
	 * The CORS {@code Access-Control-Request-Headers} request header field name.
	 * @see CORS W3C recommendation
	 */
	public static final String ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers";
	/**
	 * The CORS {@code Access-Control-Request-Method} request header field name.
	 * @see CORS W3C recommendation
	 */
	public static final String ACCESS_CONTROL_REQUEST_METHOD = "Access-Control-Request-Method";
	/**
	 * The HTTP {@code Age} header field name.
	 * @see Section 5.1 of RFC 7234
	 */
	public static final String AGE = "Age";
	/**
	 * The HTTP {@code Allow} header field name.
	 * @see Section 7.4.1 of RFC 7231
	 */
	public static final String ALLOW = "Allow";
	/**
	 * The HTTP {@code Authorization} header field name.
	 * @see Section 4.2 of RFC 7235
	 */
	public static final String AUTHORIZATION = "Authorization";
	/**
	 * The HTTP {@code Cache-Control} header field name.
	 * @see Section 5.2 of RFC 7234
	 */
	public static final String CACHE_CONTROL = "Cache-Control";
	/**
	 * The HTTP {@code Connection} header field name.
	 * @see Section 6.1 of RFC 7230
	 */
	public static final String CONNECTION = "Connection";
	/**
	 * The HTTP {@code Content-Encoding} header field name.
	 * @see Section 3.1.2.2 of RFC 7231
	 */
	public static final String CONTENT_ENCODING = "Content-Encoding";
	/**
	 * The HTTP {@code Content-Disposition} header field name
	 * @see RFC 6266
	 */
	public static final String CONTENT_DISPOSITION = "Content-Disposition";
	/**
	 * The HTTP {@code Content-Language} header field name.
	 * @see Section 3.1.3.2 of RFC 7231
	 */
	public static final String CONTENT_LANGUAGE = "Content-Language";
	/**
	 * The HTTP {@code Content-Length} header field name.
	 * @see Section 3.3.2 of RFC 7230
	 */
	public static final String CONTENT_LENGTH = "Content-Length";
	/**
	 * The HTTP {@code Content-Location} header field name.
	 * @see Section 3.1.4.2 of RFC 7231
	 */
	public static final String CONTENT_LOCATION = "Content-Location";
	/**
	 * The HTTP {@code Content-Range} header field name.
	 * @see Section 4.2 of RFC 7233
	 */
	public static final String CONTENT_RANGE = "Content-Range";
	/**
	 * The HTTP {@code Content-Type} header field name.
	 * @see Section 3.1.1.5 of RFC 7231
	 */
	public static final String CONTENT_TYPE = "Content-Type";
	/**
	 * The HTTP {@code Cookie} header field name.
	 * @see Section 4.3.4 of RFC 2109
	 */
	public static final String COOKIE = "Cookie";
	/**
	 * The HTTP {@code Date} header field name.
	 * @see Section 7.1.1.2 of RFC 7231
	 */
	public static final String DATE = "Date";
	/**
	 * The HTTP {@code ETag} header field name.
	 * @see Section 2.3 of RFC 7232
	 */
	public static final String ETAG = "ETag";
	/**
	 * The HTTP {@code Expect} header field name.
	 * @see Section 5.1.1 of RFC 7231
	 */
	public static final String EXPECT = "Expect";
	/**
	 * The HTTP {@code Expires} header field name.
	 * @see Section 5.3 of RFC 7234
	 */
	public static final String EXPIRES = "Expires";
	/**
	 * The HTTP {@code From} header field name.
	 * @see Section 5.5.1 of RFC 7231
	 */
	public static final String FROM = "From";
	/**
	 * The HTTP {@code Host} header field name.
	 * @see Section 5.4 of RFC 7230
	 */
	public static final String HOST = "Host";
	/**
	 * The HTTP {@code If-Match} header field name.
	 * @see Section 3.1 of RFC 7232
	 */
	public static final String IF_MATCH = "If-Match";
	/**
	 * The HTTP {@code If-Modified-Since} header field name.
	 * @see Section 3.3 of RFC 7232
	 */
	public static final String IF_MODIFIED_SINCE = "If-Modified-Since";
	/**
	 * The HTTP {@code If-None-Match} header field name.
	 * @see Section 3.2 of RFC 7232
	 */
	public static final String IF_NONE_MATCH = "If-None-Match";
	/**
	 * The HTTP {@code If-Range} header field name.
	 * @see Section 3.2 of RFC 7233
	 */
	public static final String IF_RANGE = "If-Range";
	/**
	 * The HTTP {@code If-Unmodified-Since} header field name.
	 * @see Section 3.4 of RFC 7232
	 */
	public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since";
	/**
	 * The HTTP {@code Last-Modified} header field name.
	 * @see Section 2.2 of RFC 7232
	 */
	public static final String LAST_MODIFIED = "Last-Modified";
	/**
	 * The HTTP {@code Link} header field name.
	 * @see RFC 5988
	 */
	public static final String LINK = "Link";
	/**
	 * The HTTP {@code Location} header field name.
	 * @see Section 7.1.2 of RFC 7231
	 */
	public static final String LOCATION = "Location";
	/**
	 * The HTTP {@code Max-Forwards} header field name.
	 * @see Section 5.1.2 of RFC 7231
	 */
	public static final String MAX_FORWARDS = "Max-Forwards";
	/**
	 * The HTTP {@code Origin} header field name.
	 * @see RFC 6454
	 */
	public static final String ORIGIN = "Origin";
	/**
	 * The HTTP {@code Pragma} header field name.
	 * @see Section 5.4 of RFC 7234
	 */
	public static final String PRAGMA = "Pragma";
	/**
	 * The HTTP {@code Proxy-Authenticate} header field name.
	 * @see Section 4.3 of RFC 7235
	 */
	public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate";
	/**
	 * The HTTP {@code Proxy-Authorization} header field name.
	 * @see Section 4.4 of RFC 7235
	 */
	public static final String PROXY_AUTHORIZATION = "Proxy-Authorization";
	/**
	 * The HTTP {@code Range} header field name.
	 * @see Section 3.1 of RFC 7233
	 */
	public static final String RANGE = "Range";
	/**
	 * The HTTP {@code Referer} header field name.
	 * @see Section 5.5.2 of RFC 7231
	 */
	public static final String REFERER = "Referer";
	/**
	 * The HTTP {@code Retry-After} header field name.
	 * @see Section 7.1.3 of RFC 7231
	 */
	public static final String RETRY_AFTER = "Retry-After";
	/**
	 * The HTTP {@code Server} header field name.
	 * @see Section 7.4.2 of RFC 7231
	 */
	public static final String SERVER = "Server";
	/**
	 * The HTTP {@code Set-Cookie} header field name.
	 * @see Section 4.2.2 of RFC 2109
	 */
	public static final String SET_COOKIE = "Set-Cookie";
	/**
	 * The HTTP {@code Set-Cookie2} header field name.
	 * @see RFC 2965
	 */
	public static final String SET_COOKIE2 = "Set-Cookie2";
	/**
	 * The HTTP {@code TE} header field name.
	 * @see Section 4.3 of RFC 7230
	 */
	public static final String TE = "TE";
	/**
	 * The HTTP {@code Trailer} header field name.
	 * @see Section 4.4 of RFC 7230
	 */
	public static final String TRAILER = "Trailer";
	/**
	 * The HTTP {@code Transfer-Encoding} header field name.
	 * @see Section 3.3.1 of RFC 7230
	 */
	public static final String TRANSFER_ENCODING = "Transfer-Encoding";
	/**
	 * The HTTP {@code Upgrade} header field name.
	 * @see Section 6.7 of RFC 7230
	 */
	public static final String UPGRADE = "Upgrade";
	/**
	 * The HTTP {@code User-Agent} header field name.
	 * @see Section 5.5.3 of RFC 7231
	 */
	public static final String USER_AGENT = "User-Agent";
	/**
	 * The HTTP {@code Vary} header field name.
	 * @see Section 7.1.4 of RFC 7231
	 */
	public static final String VARY = "Vary";
	/**
	 * The HTTP {@code Via} header field name.
	 * @see Section 5.7.1 of RFC 7230
	 */
	public static final String VIA = "Via";
	/**
	 * The HTTP {@code Warning} header field name.
	 * @see Section 5.5 of RFC 7234
	 */
	public static final String WARNING = "Warning";
	/**
	 * The HTTP {@code WWW-Authenticate} header field name.
	 * @see Section 4.1 of RFC 7235
	 */
	public static final String WWW_AUTHENTICATE = "WWW-Authenticate";

	/**
	 * Date formats as specified in the HTTP RFC
	 * @see Section 7.1.1.1 of RFC 7231
	 */
	private static final String[] DATE_FORMATS = new String[] {
			"EEE, dd MMM yyyy HH:mm:ss zzz",
			"EEE, dd-MMM-yy HH:mm:ss zzz",
			"EEE MMM dd HH:mm:ss yyyy"
	};

	/**
	 * Pattern matching ETag multiple field values in headers such as "If-Match", "If-None-Match"
	 * @see Section 2.3 of RFC 7232
	 */
	private static final Pattern ETAG_HEADER_VALUE_PATTERN = Pattern.compile("\\*|\\s*((W\\/)?(\"[^\"]*\"))\\s*,?");

	private static TimeZone GMT = TimeZone.getTimeZone("GMT");


	/**
	 * Constructs a new, empty instance of the {@code HttpHeaders} object.
	 */
	public HttpHeaders() {
		super(8);
	}
	public HttpHeaders(Map> headers) {
		super(headers);
	}

	/**
	 * Set the (new) value of the {@code Access-Control-Allow-Credentials} response header.
	 */
	public void setAccessControlAllowCredentials(boolean allowCredentials) {
		set(ACCESS_CONTROL_ALLOW_CREDENTIALS, Boolean.toString(allowCredentials));
	}

	/**
	 * Return the value of the {@code Access-Control-Allow-Credentials} response header.
	 */
	public boolean getAccessControlAllowCredentials() {
		return Boolean.parseBoolean(getFirst(ACCESS_CONTROL_ALLOW_CREDENTIALS));
	}

	/**
	 * Set the (new) value of the {@code Access-Control-Allow-Headers} response header.
	 */
	public void setAccessControlAllowHeaders(List allowedHeaders) {
		set(ACCESS_CONTROL_ALLOW_HEADERS, toCommaDelimitedString(allowedHeaders));
	}

	/**
	 * Return the value of the {@code Access-Control-Allow-Headers} response header.
	 */
	public List getAccessControlAllowHeaders() {
		return getValuesAsList(ACCESS_CONTROL_ALLOW_HEADERS);
	}

	/**
	 * Set the (new) value of the {@code Access-Control-Allow-Methods} response header.
	 */
	public void setAccessControlAllowMethods(List allowedMethods) {
		set(ACCESS_CONTROL_ALLOW_METHODS, Joiner.on(StrUtil.COMMA).join(allowedMethods));
	}

	/**
	 * Return the value of the {@code Access-Control-Allow-Methods} response header.
	 */
	public List getAccessControlAllowMethods() {
		List result = new ArrayList();
		String value = getFirst(ACCESS_CONTROL_ALLOW_METHODS);
		if (value != null) {
			String[] tokens = value.split(StrUtil.COMMA);
			for (String token : tokens) {
				Method resolved = Method.valueOf(token);
				result.add(resolved);
			}
		}
		return result;
	}

	/**
	 * Set the (new) value of the {@code Access-Control-Allow-Origin} response header.
	 */
	public void setAccessControlAllowOrigin(String allowedOrigin) {
		set(ACCESS_CONTROL_ALLOW_ORIGIN, allowedOrigin);
	}

	/**
	 * Return the value of the {@code Access-Control-Allow-Origin} response header.
	 */
	public String getAccessControlAllowOrigin() {
		return getFieldValues(ACCESS_CONTROL_ALLOW_ORIGIN);
	}

	/**
	 * Set the (new) value of the {@code Access-Control-Expose-Headers} response header.
	 */
	public void setAccessControlExposeHeaders(List exposedHeaders) {
		set(ACCESS_CONTROL_EXPOSE_HEADERS, toCommaDelimitedString(exposedHeaders));
	}

	/**
	 * Return the value of the {@code Access-Control-Expose-Headers} response header.
	 */
	public List getAccessControlExposeHeaders() {
		return getValuesAsList(ACCESS_CONTROL_EXPOSE_HEADERS);
	}

	/**
	 * Set the (new) value of the {@code Access-Control-Max-Age} response header.
	 */
	public void setAccessControlMaxAge(long maxAge) {
		set(ACCESS_CONTROL_MAX_AGE, Long.toString(maxAge));
	}

	/**
	 * Return the value of the {@code Access-Control-Max-Age} response header.
	 * 

Returns -1 when the max age is unknown. */ public long getAccessControlMaxAge() { String value = getFirst(ACCESS_CONTROL_MAX_AGE); return (value != null ? Long.parseLong(value) : -1); } /** * Set the (new) value of the {@code Access-Control-Request-Headers} request header. */ public void setAccessControlRequestHeaders(List requestHeaders) { set(ACCESS_CONTROL_REQUEST_HEADERS, toCommaDelimitedString(requestHeaders)); } /** * Return the value of the {@code Access-Control-Request-Headers} request header. */ public List getAccessControlRequestHeaders() { return getValuesAsList(ACCESS_CONTROL_REQUEST_HEADERS); } /** * Set the (new) value of the {@code Access-Control-Request-Method} request header. */ public void setAccessControlRequestMethod(Method requestMethod) { set(ACCESS_CONTROL_REQUEST_METHOD, requestMethod.name()); } /** * Return the value of the {@code Access-Control-Request-Method} request header. */ public Method getAccessControlRequestMethod() { return Method.valueOf(getFirst(ACCESS_CONTROL_REQUEST_METHOD)); } /** * Set the list of acceptable {@linkplain Charset charsets}, * as specified by the {@code Accept-Charset} header. */ public void setAcceptCharset(List acceptableCharsets) { StringBuilder builder = new StringBuilder(); for (Iterator iterator = acceptableCharsets.iterator(); iterator.hasNext();) { Charset charset = iterator.next(); builder.append(charset.name().toLowerCase(Locale.ENGLISH)); if (iterator.hasNext()) { builder.append(", "); } } set(ACCEPT_CHARSET, builder.toString()); } /** * Return the list of acceptable {@linkplain Charset charsets}, * as specified by the {@code Accept-Charset} header. */ public List getAcceptCharset() { String value = getFirst(ACCEPT_CHARSET); if (value != null) { String[] tokens = value.split(StrUtil.COMMA); List result = new ArrayList(tokens.length); for (String token : tokens) { int paramIdx = token.indexOf(';'); String charsetName; if (paramIdx == -1) { charsetName = token; } else { charsetName = token.substring(0, paramIdx); } if (!charsetName.equals("*")) { result.add(Charset.forName(charsetName)); } } return result; } else { return Collections.emptyList(); } } /** * Set the set of allowed {@link Method HTTP methods}, * as specified by the {@code Allow} header. */ public void setAllow(Set allowedMethods) { set(ALLOW, Joiner.on(StrUtil.COMMA).join(allowedMethods)); } /** * Return the set of allowed {@link Method HTTP methods}, * as specified by the {@code Allow} header. *

Returns an empty set when the allowed methods are unspecified. */ public Set getAllow() { String value = getFirst(ALLOW); if (!StrUtil.isEmpty(value)) { String[] tokens = value.split(StrUtil.COMMA); List result = new ArrayList(tokens.length); for (String token : tokens) { Method resolved = Method.valueOf(token); result.add(resolved); } return EnumSet.copyOf(result); } else { return EnumSet.noneOf(Method.class); } } /** * Set the (new) value of the {@code Cache-Control} header. */ public void setCacheControl(String cacheControl) { set(CACHE_CONTROL, cacheControl); } /** * Return the value of the {@code Cache-Control} header. */ public String getCacheControl() { return getFieldValues(CACHE_CONTROL); } /** * Set the (new) value of the {@code Connection} header. */ public void setConnection(String connection) { set(CONNECTION, connection); } /** * Set the (new) value of the {@code Connection} header. */ public void setConnection(List connection) { set(CONNECTION, toCommaDelimitedString(connection)); } /** * Return the value of the {@code Connection} header. */ public List getConnection() { return getValuesAsList(CONNECTION); } /** * Set the {@code Content-Disposition} header when creating a * {@code "multipart/form-data"} request. *

Applications typically would not set this header directly but * rather prepare a {@code MultiValueMap}, * and then pass that to the {@code RestTemplate} or {@code WebClient}. * @param name the control name * @param filename the filename (may be {@code null}) */ public void setContentDispositionFormData(String name, String filename) { StringBuilder builder = new StringBuilder("form-data; name=\""); builder.append(name).append('\"'); if (filename != null) { builder.append("; filename=\""); builder.append(filename).append('\"'); } set(CONTENT_DISPOSITION, builder.toString()); } /** * Set the length of the body in bytes, as specified by the * {@code Content-Length} header. */ public void setContentLength(long contentLength) { set(CONTENT_LENGTH, Long.toString(contentLength)); } /** * Return the length of the body in bytes, as specified by the * {@code Content-Length} header. *

Returns -1 when the content-length is unknown. */ public long getContentLength() { String value = getFirst(CONTENT_LENGTH); return (value != null ? Long.parseLong(value) : -1); } /** * Set the {@linkplain MediaType media type} of the body, * as specified by the {@code Content-Type} header. */ public void setContentType(MediaType mediaType) { set(CONTENT_TYPE, mediaType.toString()); } /** * Return the {@linkplain MediaType media type} of the body, as specified * by the {@code Content-Type} header. *

Returns {@code null} when the content-type is unknown. */ public MediaType getContentType() { String value = getFirst(CONTENT_TYPE); return (StrUtil.isNotEmpty(value) ? MediaType.parse(value) : null); } /** * Set the date and time at which the message was created, as specified * by the {@code Date} header. *

The date should be specified as the number of milliseconds since * January 1, 1970 GMT. */ public void setDate(long date) { setDate(DATE, date); } /** * Return the date and time at which the message was created, as specified * by the {@code Date} header. *

The date is returned as the number of milliseconds since * January 1, 1970 GMT. Returns -1 when the date is unknown. * @throws IllegalArgumentException if the value can't be converted to a date */ public long getDate() { return getFirstDate(DATE); } /** * Set the (new) entity tag of the body, as specified by the {@code ETag} header. */ public void setETag(String eTag) { set(ETAG, eTag); } /** * Return the entity tag of the body, as specified by the {@code ETag} header. */ public String getETag() { return getFirst(ETAG); } /** * Set the date and time at which the message is no longer valid, * as specified by the {@code Expires} header. *

The date should be specified as the number of milliseconds since * January 1, 1970 GMT. */ public void setExpires(long expires) { setDate(EXPIRES, expires); } /** * Return the date and time at which the message is no longer valid, * as specified by the {@code Expires} header. *

The date is returned as the number of milliseconds since * January 1, 1970 GMT. Returns -1 when the date is unknown. */ public long getExpires() { return getFirstDate(EXPIRES, false); } /** * Set the (new) value of the {@code If-Match} header. * @since 4.3 */ public void setIfMatch(String ifMatch) { set(IF_MATCH, ifMatch); } /** * Set the (new) value of the {@code If-Match} header. * @since 4.3 */ public void setIfMatch(List ifMatchList) { set(IF_MATCH, toCommaDelimitedString(ifMatchList)); } /** * Return the value of the {@code If-Match} header. * @since 4.3 */ public List getIfMatch() { return getETagValuesAsList(IF_MATCH); } /** * Set the (new) value of the {@code If-Modified-Since} header. *

The date should be specified as the number of milliseconds since * January 1, 1970 GMT. */ public void setIfModifiedSince(long ifModifiedSince) { setDate(IF_MODIFIED_SINCE, ifModifiedSince); } /** * Return the value of the {@code If-Modified-Since} header. *

The date is returned as the number of milliseconds since * January 1, 1970 GMT. Returns -1 when the date is unknown. */ public long getIfModifiedSince() { return getFirstDate(IF_MODIFIED_SINCE, false); } /** * Set the (new) value of the {@code If-None-Match} header. */ public void setIfNoneMatch(String ifNoneMatch) { set(IF_NONE_MATCH, ifNoneMatch); } /** * Set the (new) values of the {@code If-None-Match} header. */ public void setIfNoneMatch(List ifNoneMatchList) { set(IF_NONE_MATCH, toCommaDelimitedString(ifNoneMatchList)); } /** * Return the value of the {@code If-None-Match} header. */ public List getIfNoneMatch() { return getETagValuesAsList(IF_NONE_MATCH); } /** * Set the (new) value of the {@code If-Unmodified-Since} header. *

The date should be specified as the number of milliseconds since * January 1, 1970 GMT. * @since 4.3 */ public void setIfUnmodifiedSince(long ifUnmodifiedSince) { setDate(IF_UNMODIFIED_SINCE, ifUnmodifiedSince); } /** * Return the value of the {@code If-Unmodified-Since} header. *

The date is returned as the number of milliseconds since * January 1, 1970 GMT. Returns -1 when the date is unknown. * @since 4.3 */ public long getIfUnmodifiedSince() { return getFirstDate(IF_UNMODIFIED_SINCE, false); } /** * Set the time the resource was last changed, as specified by the * {@code Last-Modified} header. *

The date should be specified as the number of milliseconds since * January 1, 1970 GMT. */ public void setLastModified(long lastModified) { setDate(LAST_MODIFIED, lastModified); } /** * Return the time the resource was last changed, as specified by the * {@code Last-Modified} header. *

The date is returned as the number of milliseconds since * January 1, 1970 GMT. Returns -1 when the date is unknown. */ public long getLastModified() { return getFirstDate(LAST_MODIFIED, false); } /** * Set the (new) location of a resource, * as specified by the {@code Location} header. */ public void setLocation(URI location) { set(LOCATION, location.toASCIIString()); } /** * Return the (new) location of a resource * as specified by the {@code Location} header. *

Returns {@code null} when the location is unknown. */ public URI getLocation() { String value = getFirst(LOCATION); return (value != null ? URI.create(value) : null); } /** * Set the (new) value of the {@code Origin} header. */ public void setOrigin(String origin) { set(ORIGIN, origin); } /** * Return the value of the {@code Origin} header. */ public String getOrigin() { return getFirst(ORIGIN); } /** * Set the (new) value of the {@code Pragma} header. */ public void setPragma(String pragma) { set(PRAGMA, pragma); } /** * Return the value of the {@code Pragma} header. */ public String getPragma() { return getFirst(PRAGMA); } /** * Set the (new) value of the {@code Upgrade} header. */ public void setUpgrade(String upgrade) { set(UPGRADE, upgrade); } /** * Return the value of the {@code Upgrade} header. */ public String getUpgrade() { return getFirst(UPGRADE); } /** * Set the request header names (e.g. "Accept-Language") for which the * response is subject to content negotiation and variances based on the * value of those request headers. * @param requestHeaders the request header names * @since 4.3 */ public void setVary(List requestHeaders) { set(VARY, toCommaDelimitedString(requestHeaders)); } /** * Return the request header names subject to content negotiation. * @since 4.3 */ public List getVary() { return getValuesAsList(VARY); } /** * Set the given date under the given header name after formatting it as a string * using the pattern {@code "EEE, dd MMM yyyy HH:mm:ss zzz"}. * @since 3.2.4 */ public void setDate(String headerName, long date) { SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMATS[0], Locale.US); dateFormat.setTimeZone(GMT); set(headerName, dateFormat.format(new Date(date))); } /** * Parse the first header value for the given header name as a date, * return -1 if there is no value, or raise {@link IllegalArgumentException} * if the value cannot be parsed as a date. * @param headerName the header name * @return the parsed date header, or -1 if none * @since 3.2.4 */ public long getFirstDate(String headerName) { return getFirstDate(headerName, true); } /** * Parse the first header value for the given header name as a date, * return -1 if there is no value or also in case of an invalid value * (if {@code rejectInvalid=false}), or raise {@link IllegalArgumentException} * if the value cannot be parsed as a date. * @param headerName the header name * @param rejectInvalid whether to reject invalid values with an * {@link IllegalArgumentException} ({@code true}) or rather return -1 * in that case ({@code false}) * @return the parsed date header, or -1 if none (or invalid) */ private long getFirstDate(String headerName, boolean rejectInvalid) { String headerValue = getFirst(headerName); if (headerValue == null) { // No header value sent at all return -1; } if (headerValue.length() >= 3) { // Short "0" or "-1" like values are never valid HTTP date headers... // Let's only bother with SimpleDateFormat parsing for long enough values. for (String dateFormat : DATE_FORMATS) { SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat, Locale.US); simpleDateFormat.setTimeZone(GMT); try { return simpleDateFormat.parse(headerValue).getTime(); } catch (ParseException ex) { // ignore } } } if (rejectInvalid) { throw new IllegalArgumentException("Cannot parse date value \"" + headerValue + "\" for \"" + headerName + "\" header"); } return -1; } /** * Return all values of a given header name, * even if this header is set multiple times. * @param headerName the header name * @return all associated values * @since 4.3 */ public List getValuesAsList(String headerName) { List values = get(headerName); if (values != null) { List result = new ArrayList(); for (String value : values) { if (value != null) { String[] tokens = value.split(StrUtil.COMMA); result.addAll(Arrays.asList(tokens)); } } return result; } return Collections.emptyList(); } /** * Retrieve a combined result from the field values of the ETag header. * @param headerName the header name * @return the combined result * @since 4.3 */ protected List getETagValuesAsList(String headerName) { List values = get(headerName); if (values != null) { List result = new ArrayList(); for (String value : values) { if (value != null) { Matcher matcher = ETAG_HEADER_VALUE_PATTERN.matcher(value); while (matcher.find()) { if ("*".equals(matcher.group())) { result.add(matcher.group()); } else { result.add(matcher.group(1)); } } if (result.isEmpty()) { throw new IllegalArgumentException( "Could not parse header '" + headerName + "' with value '" + value + "'"); } } } return result; } return Collections.emptyList(); } /** * Retrieve a combined result from the field values of multi-valued headers. * @param headerName the header name * @return the combined result * @since 4.3 */ protected String getFieldValues(String headerName) { List headerValues = get(headerName); return (headerValues != null ? toCommaDelimitedString(headerValues) : null); } /** * Turn the given list of header values into a comma-delimited result. * @param headerValues the list of header values * @return a combined result with comma delimitation */ protected String toCommaDelimitedString(List headerValues) { StringBuilder builder = new StringBuilder(); for (Iterator it = headerValues.iterator(); it.hasNext(); ) { String val = it.next(); builder.append(val); if (it.hasNext()) { builder.append(", "); } } return builder.toString(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy