/*
* Copyright (C) 2015 SoftIndex LLC.
*
* 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 io.datakernel.http;
import io.datakernel.bytebuf.ByteBuf;
import io.datakernel.bytebuf.ByteBufPool;
import io.datakernel.bytebuf.ByteBufStrings;
import java.util.Date;
import java.util.List;
import static io.datakernel.bytebuf.ByteBufStrings.*;
@SuppressWarnings("unused")
public final class HttpHeaders {
private static final CaseInsensitiveTokenMap<HttpHeader> headers = new CaseInsensitiveTokenMap<HttpHeader>(512, 2, HttpHeader.class) {
@Override
protected HttpHeader create(byte[] bytes, int offset, int length, byte[] lowerCaseBytes, int lowerCaseHashCode) {
return new HttpHeader(bytes, offset, length, lowerCaseBytes, lowerCaseHashCode);
}
};
public static final HttpHeader CACHE_CONTROL = headers.register("Cache-Control");
public static final HttpHeader CONTENT_LENGTH = headers.register("Content-Length");
public static final HttpHeader CONTENT_TYPE = headers.register("Content-Type");
public static final HttpHeader DATE = headers.register("Date");
public static final HttpHeader PRAGMA = headers.register("Pragma");
public static final HttpHeader VIA = headers.register("Via");
public static final HttpHeader WARNING = headers.register("Warning");
public static final HttpHeader ACCEPT = headers.register("Accept");
public static final HttpHeader ACCEPT_CHARSET = headers.register("Accept-Charset");
public static final HttpHeader ACCEPT_ENCODING = headers.register("Accept-Encoding");
public static final HttpHeader ACCEPT_LANGUAGE = headers.register("Accept-Language");
public static final HttpHeader ACCESS_CONTROL_REQUEST_HEADERS = headers.register("Access-Control-Request-Headers");
public static final HttpHeader ACCESS_CONTROL_REQUEST_METHOD = headers.register("Access-Control-Request-Method");
public static final HttpHeader AUTHORIZATION = headers.register("Authorization");
public static final HttpHeader CONNECTION = headers.register("Connection");
public static final HttpHeader COOKIE = headers.register("Cookie");
public static final HttpHeader EXPECT = headers.register("Expect");
public static final HttpHeader FROM = headers.register("From");
public static final HttpHeader FOLLOW_ONLY_WHEN_PRERENDER_SHOWN = headers.register("Follow-Only-When-Prerender-Shown");
public static final HttpHeader HOST = headers.register("Host");
public static final HttpHeader IF_MATCH = headers.register("If-Match");
public static final HttpHeader IF_MODIFIED_SINCE = headers.register("If-Modified-Since");
public static final HttpHeader IF_NONE_MATCH = headers.register("If-None-Match");
public static final HttpHeader IF_RANGE = headers.register("If-Range");
public static final HttpHeader IF_UNMODIFIED_SINCE = headers.register("If-Unmodified-Since");
public static final HttpHeader LAST_EVENT_ID = headers.register("Last-Event-ID");
public static final HttpHeader MAX_FORWARDS = headers.register("Max-Forwards");
public static final HttpHeader ORIGIN = headers.register("Origin");
public static final HttpHeader PROXY_AUTHORIZATION = headers.register("Proxy-Authorization");
public static final HttpHeader RANGE = headers.register("Range");
public static final HttpHeader REFERER = headers.register("Referer");
public static final HttpHeader TE = headers.register("TE");
public static final HttpHeader UPGRADE = headers.register("Upgrade");
public static final HttpHeader USER_AGENT = headers.register("User-Agent");
public static final HttpHeader ACCEPT_RANGES = headers.register("Accept-Ranges");
public static final HttpHeader ACCESS_CONTROL_ALLOW_HEADERS = headers.register("Access-Control-Allow-Headers");
public static final HttpHeader ACCESS_CONTROL_ALLOW_METHODS = headers.register("Access-Control-Allow-Methods");
public static final HttpHeader ACCESS_CONTROL_ALLOW_ORIGIN = headers.register("Access-Control-Allow-Origin");
public static final HttpHeader ACCESS_CONTROL_ALLOW_CREDENTIALS = headers.register("Access-Control-Allow-Credentials");
public static final HttpHeader ACCESS_CONTROL_EXPOSE_HEADERS = headers.register("Access-Control-Expose-Headers");
public static final HttpHeader ACCESS_CONTROL_MAX_AGE = headers.register("Access-Control-Max-Age");
public static final HttpHeader AGE = headers.register("Age");
public static final HttpHeader ALLOW = headers.register("Allow");
public static final HttpHeader CONTENT_DISPOSITION = headers.register("Content-Disposition");
public static final HttpHeader CONTENT_ENCODING = headers.register("Content-Encoding");
public static final HttpHeader CONTENT_LANGUAGE = headers.register("Content-Language");
public static final HttpHeader CONTENT_LOCATION = headers.register("Content-Location");
public static final HttpHeader CONTENT_MD5 = headers.register("Content-MD5");
public static final HttpHeader CONTENT_RANGE = headers.register("Content-Range");
public static final HttpHeader CONTENT_SECURITY_POLICY = headers.register("Content-Security-Policy");
public static final HttpHeader CONTENT_SECURITY_POLICY_REPORT_ONLY = headers.register("Content-Security-Policy-Report-Only");
public static final HttpHeader ETAG = headers.register("ETag");
public static final HttpHeader EXPIRES = headers.register("Expires");
public static final HttpHeader LAST_MODIFIED = headers.register("Last-Modified");
public static final HttpHeader LINK = headers.register("Link");
public static final HttpHeader LOCATION = headers.register("Location");
public static final HttpHeader P3P = headers.register("P3P");
public static final HttpHeader PROXY_AUTHENTICATE = headers.register("Proxy-Authenticate");
public static final HttpHeader REFRESH = headers.register("Refresh");
public static final HttpHeader RETRY_AFTER = headers.register("Retry-After");
public static final HttpHeader SERVER = headers.register("Server");
public static final HttpHeader SET_COOKIE = headers.register("Set-Cookie");
@Deprecated
public static final HttpHeader SET_COOKIE2 = headers.register("Set-Cookie2");
public static final HttpHeader STRICT_TRANSPORT_SECURITY = headers.register("Strict-Transport-Security");
public static final HttpHeader TIMING_ALLOW_ORIGIN = headers.register("Timing-Allow-Origin");
public static final HttpHeader TRAILER = headers.register("Trailer");
public static final HttpHeader TRANSFER_ENCODING = headers.register("Transfer-Encoding");
public static final HttpHeader VARY = headers.register("Vary");
public static final HttpHeader WWW_AUTHENTICATE = headers.register("WWW-Authenticate");
public static final HttpHeader DNT = headers.register("DNT");
public static final HttpHeader X_CONTENT_TYPE_OPTIONS = headers.register("X-Content-Type-Options");
public static final HttpHeader X_DO_NOT_TRACK = headers.register("X-Do-Not-Track");
public static final HttpHeader X_FORWARDED_FOR = headers.register("X-Forwarded-For");
public static final HttpHeader X_FORWARDED_PROTO = headers.register("X-Forwarded-Proto");
public static final HttpHeader X_FRAME_OPTIONS = headers.register("X-Frame-Options");
public static final HttpHeader X_POWERED_BY = headers.register("X-Powered-By");
public static final HttpHeader PUBLIC_KEY_PINS = headers.register("Public-Key-Pins");
public static final HttpHeader PUBLIC_KEY_PINS_REPORT_ONLY = headers.register("Public-Key-Pins-Report-Only");
public static final HttpHeader X_REQUESTED_WITH = headers.register("X-Requested-With");
public static final HttpHeader X_USER_IP = headers.register("X-User-IP");
public static final HttpHeader X_XSS_PROTECTION = headers.register("X-XSS-Protection");
public static final HttpHeader X_REAL_IP = headers.register("X-Real-IP");
public static final HttpHeader X_AUTH_TOKEN = headers.register("X-Auth-Token");
static HttpHeader of(byte[] array, int offset, int length, int lowerCaseHashCode) {
return headers.getOrCreate(array, offset, length, lowerCaseHashCode);
}
public static HttpHeader of(String string) {
byte[] array = encodeAscii(string);
return headers.getOrCreate(array, 0, array.length);
}
// helpers
static Value asBytes(HttpHeader key, byte[] array, int offset, int size) {
return new ValueOfBytes(key, array, offset, size);
}
static Value asBytes(HttpHeader key, byte[] array) {
return asBytes(key, array, 0, array.length);
}
static Value asBytes(HttpHeader key, String string) {
return asBytes(key, encodeAscii(string));
}
static Value ofDecimal(HttpHeader key, int value) {
return new ValueOfUnsignedDecimal(key, value);
}
static Value ofString(HttpHeader key, String string) {
return new ValueOfString(key, string);
}
static Value ofCharsets(HttpHeader key, List<AcceptCharset> charsets) {
return new ValueOfCharsets(key, charsets);
}
static Value ofCookies(HttpHeader key, List<HttpCookie> cookies) {
return new ValueOfSimpleCookies(key, cookies);
}
static Value ofSetCookies(HttpHeader key, List<HttpCookie> cookies) {
return new ValueOfFullCookies(key, cookies);
}
static Value ofDate(HttpHeader key, Date date) {
return new ValueOfDate(key, date);
}
static Value ofAcceptContentTypes(HttpHeader key, List<AcceptMediaType> type) {
return new ValueOfAcceptContentTypes(key, type);
}
static Value ofContentType(HttpHeader key, ContentType type) {
return new ValueOfContentType(key, type);
}
static abstract class Value {
private final HttpHeader key;
public Value(HttpHeader key) {
this.key = key;
}
public abstract int estimateSize();
public abstract void writeTo(ByteBuf buf);
public HttpHeader getKey() {
return key;
}
}
static final class ValueOfBytes extends Value {
final byte[] array;
final int offset;
final int size;
private ValueOfBytes(HttpHeader key, byte[] array, int offset, int size) {
super(key);
this.array = array;
this.offset = offset;
this.size = size;
}
@Override
public int estimateSize() {
return size;
}
@Override
public void writeTo(ByteBuf buf) {
buf.put(array, offset, size);
}
@Override
public String toString() {
return decodeAscii(array, offset, size);
}
}
private static final class ValueOfContentType extends Value {
private ContentType type;
public ValueOfContentType(HttpHeader key, ContentType type) {
super(key);
this.type = type;
}
@Override
public int estimateSize() {
return type.size();
}
@Override
public void writeTo(ByteBuf buf) {
ContentType.render(type, buf);
}
@Override
public String toString() {
ByteBuf buf = ByteBufPool.allocate(estimateSize());
ContentType.render(type, buf);
String value = ByteBufStrings.decodeAscii(buf);
buf.recycle();
return value;
}
}
private static final class ValueOfAcceptContentTypes extends Value {
private final List<AcceptMediaType> types;
public ValueOfAcceptContentTypes(HttpHeader key, List<AcceptMediaType> types) {
super(key);
this.types = types;
}
@Override
public int estimateSize() {
int size = 0;
for (AcceptMediaType type : types) {
size += type.estimateSize() + 2;
}
return size;
}
@Override
public void writeTo(ByteBuf buf) {
AcceptMediaType.render(types, buf);
}
@Override
public String toString() {
ByteBuf buf = ByteBufPool.allocate(estimateSize());
AcceptMediaType.render(types, buf);
String value = ByteBufStrings.decodeAscii(buf);
buf.recycle();
return value;
}
}
private static final class ValueOfSimpleCookies extends Value {
private final List<HttpCookie> cookies;
public ValueOfSimpleCookies(HttpHeader key, List<HttpCookie> cookies) {
super(key);
this.cookies = cookies;
}
@Override
public int estimateSize() {
int size = 0;
for (HttpCookie cookie : cookies) {
size += cookie.getName().length();
size += cookie.getValue() == null ? 0 : cookie.getValue().length() + 1;
}
size += (cookies.size() - 1) * 2; // semicolons and spaces
return size;
}
@Override
public void writeTo(ByteBuf buf) {
HttpCookie.renderSimple(cookies, buf);
}
@Override
public String toString() {
ByteBuf buf = ByteBufPool.allocate(estimateSize());
HttpCookie.renderSimple(cookies, buf);
String value = ByteBufStrings.decodeAscii(buf);
buf.recycle();
return value;
}
}
private static final class ValueOfFullCookies extends Value {
private final List<HttpCookie> cookies;
public ValueOfFullCookies(HttpHeader key, List<HttpCookie> cookies) {
super(key);
this.cookies = cookies;
}
@Override
public int estimateSize() {
int size = 0;
for (HttpCookie cookie : cookies) {
size += cookie.getName().length();
size += cookie.getValue() == null ? 0 : cookie.getValue().length() + 1;
size += cookie.getDomain() == null ? 0 : cookie.getDomain().length() + 10;
size += cookie.getPath() == null ? 0 : cookie.getPath().length() + 6;
size += cookie.getExtension() == null ? 0 : cookie.getExtension().length();
size += 102;
}
size += (cookies.size() - 1) * 2;
return size;
}
@Override
public void writeTo(ByteBuf buf) {
HttpCookie.renderFull(cookies, buf);
}
@Override
public String toString() {
ByteBuf buf = ByteBufPool.allocate(estimateSize());
HttpCookie.renderFull(cookies, buf);
String value = ByteBufStrings.decodeAscii(buf);
buf.recycle();
return value;
}
}
private static final class ValueOfCharsets extends Value {
private final List<AcceptCharset> charsets;
public ValueOfCharsets(HttpHeader key, List<AcceptCharset> charsets) {
super(key);
this.charsets = charsets;
}
@Override
public int estimateSize() {
int size = 0;
for (AcceptCharset charset : charsets) {
size += charset.estimateSize() + 2;
}
return size;
}
@Override
public void writeTo(ByteBuf buf) {
AcceptCharset.render(charsets, buf);
}
@Override
public String toString() {
ByteBuf buf = ByteBufPool.allocate(estimateSize());
AcceptCharset.render(charsets, buf);
String value = ByteBufStrings.decodeAscii(buf);
buf.recycle();
return value;
}
}
private static final class ValueOfDate extends Value {
private final Date date;
public ValueOfDate(HttpHeader key, Date date) {
super(key);
this.date = date;
}
@Override
public int estimateSize() {
return 29;
}
@Override
public void writeTo(ByteBuf buf) {
HttpDate.render(date.getTime(), buf);
}
@Override
public String toString() {
ByteBuf buf = ByteBufPool.allocate(estimateSize());
HttpDate.render(date.getTime(), buf);
String value = ByteBufStrings.decodeAscii(buf);
buf.recycle();
return value;
}
}
private static final class ValueOfUnsignedDecimal extends Value {
private final int value;
private ValueOfUnsignedDecimal(HttpHeader header, int value) {
super(header);
this.value = value;
}
@Override
public int estimateSize() {
return 10; // Integer.toString(Integer.MAX_VALUE).length();
}
@Override
public void writeTo(ByteBuf buf) {
putDecimal(buf, value);
}
@Override
public String toString() {
return Integer.toString(value);
}
}
private static final class ValueOfString extends Value {
private final String string;
private ValueOfString(HttpHeader key, String string) {
super(key);
this.string = string;
}
@Override
public int estimateSize() {
return string.length();
}
@Override
public void writeTo(ByteBuf buf) {
putAscii(buf, string);
}
@Override
public String toString() {
return string;
}
}
}