/* * 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.ByteBufStrings; import io.datakernel.exception.ParseException; import java.nio.charset.Charset; import static io.datakernel.bytebuf.ByteBufStrings.encodeAscii; import static io.datakernel.http.ContentTypes.lookup; import static io.datakernel.http.HttpUtils.skipSpaces; public final class ContentType { private static final byte[] CHARSET_KEY = encodeAscii("charset"); final MediaType mime; final HttpCharset charset; ContentType(MediaType mime, HttpCharset charset) { this.mime = mime; this.charset = charset; } public static ContentType of(MediaType mime) { return new ContentType(mime, null); } public static ContentType of(MediaType mime, Charset charset) { return lookup(mime, HttpCharset.of(charset)); } static ContentType parse(byte[] bytes, int pos, int length) throws ParseException { try { // parsing media type int end = pos + length; pos = skipSpaces(bytes, pos, end); int start = pos; int lowerCaseHashCode = 1; while (pos < end && bytes[pos] != ';') { byte b = bytes[pos]; if (b >= 'A' && b <= 'Z') { b += 'a' - 'A'; } lowerCaseHashCode = lowerCaseHashCode * 31 + b; pos++; } MediaType type = MediaTypes.of(bytes, start, pos - start, lowerCaseHashCode); pos++; // parsing parameters if any (interested in 'charset' only) HttpCharset charset = null; if (pos < end) { pos = skipSpaces(bytes, pos, end); start = pos; while (pos < end) { if (bytes[pos] == '=' && ByteBufStrings.equalsLowerCaseAscii(CHARSET_KEY, bytes, start, pos - start)) { pos++; start = pos; while (pos < end && bytes[pos] != ';') { pos++; } charset = HttpCharset.parse(bytes, start, pos - start); } else if (bytes[pos] == ';' && pos + 1 < end) { start = skipSpaces(bytes, pos + 1, end); } pos++; } } return lookup(type, charset); } catch (RuntimeException e) { throw new ParseException(); } } static void render(ContentType type, ByteBuf buf) { int pos = render(type, buf.array(), buf.writePosition()); buf.writePosition(pos); } static int render(ContentType type, byte[] container, int pos) { pos += MediaTypes.render(type.getMediaType(), container, pos); if (type.charset != null) { container[pos++] = ';'; container[pos++] = ' '; System.arraycopy(CHARSET_KEY, 0, container, pos, CHARSET_KEY.length); pos += CHARSET_KEY.length; container[pos++] = '='; pos += HttpCharset.render(type.charset, container, pos); } return pos; } public Charset getCharset() throws ParseException { return charset == null ? null : charset.toJavaCharset(); } public MediaType getMediaType() { return mime; } int size() { int size = mime.size(); if (charset != null) { size += charset.size(); size += 10; // '; charset=' } return size; } @Override public String toString() { return "ContentType{" + "type=" + mime + ", charset=" + charset + '}'; } }